You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 1-js/04-object-basics/08-symbol/article.md
+1-1Lines changed: 1 addition & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -169,7 +169,7 @@ for (let key in user) alert(key); // name, age (no symbols)
169
169
*/!*
170
170
171
171
// the direct access by the symbol works
172
-
alert( "Direct: "+ user[id] );
172
+
alert( "Direct: "+ user[id] );// Direct: 123
173
173
```
174
174
175
175
[Object.keys(user)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys) also ignores them. That's a part of the general "hiding symbolic properties" principle. If another script or a library loops over our object, it won't unexpectedly access a symbolic property.
Copy file name to clipboardExpand all lines: 1-js/08-prototypes/04-prototype-methods/article.md
+60-41Lines changed: 60 additions & 41 deletions
Original file line number
Diff line number
Diff line change
@@ -3,15 +3,18 @@
3
3
4
4
In the first chapter of this section, we mentioned that there are modern methods to setup a prototype.
5
5
6
-
The `__proto__` is considered outdated and somewhat deprecated (in browser-only part of the JavaScript standard).
6
+
Setting or reading the prototype with `obj.__proto__` is considered outdated and somewhat deprecated (moved to the so-called "Annex B" of the JavaScript standard, meant for browsers only).
7
7
8
-
The modern methods are:
8
+
The modern methods to get/set a prototype are:
9
9
10
-
-[Object.create(proto, [descriptors])](mdn:js/Object/create) -- creates an empty object with given `proto` as `[[Prototype]]` and optional property descriptors.
11
10
-[Object.getPrototypeOf(obj)](mdn:js/Object/getPrototypeOf) -- returns the `[[Prototype]]` of `obj`.
12
11
-[Object.setPrototypeOf(obj, proto)](mdn:js/Object/setPrototypeOf) -- sets the `[[Prototype]]` of `obj` to `proto`.
13
12
14
-
These should be used instead of `__proto__`.
13
+
The only usage of `__proto__`, that's not frowned upon, is as a property when creating a new object: `{ __proto__: ... }`.
14
+
15
+
Although, there's a special method for this too:
16
+
17
+
-[Object.create(proto, [descriptors])](mdn:js/Object/create) -- creates an empty object with given `proto` as `[[Prototype]]` and optional property descriptors.
15
18
16
19
For instance:
17
20
@@ -22,7 +25,7 @@ let animal = {
22
25
23
26
// create a new object with animal as a prototype
24
27
*!*
25
-
let rabbit =Object.create(animal);
28
+
let rabbit =Object.create(animal);// same as {__proto__: animal}
26
29
*/!*
27
30
28
31
alert(rabbit.eats); // true
@@ -36,7 +39,9 @@ Object.setPrototypeOf(rabbit, {}); // change the prototype of rabbit to {}
36
39
*/!*
37
40
```
38
41
39
-
`Object.create` has an optional second argument: property descriptors. We can provide additional properties to the new object there, like this:
42
+
The `Object.create` method is a bit more powerful, as it has an optional second argument: property descriptors.
43
+
44
+
We can provide additional properties to the new object there, like this:
40
45
41
46
```js run
42
47
let animal = {
@@ -57,26 +62,34 @@ The descriptors are in the same format as described in the chapter <info:propert
57
62
We can use `Object.create` to perform an object cloning more powerful than copying properties in `for..in`:
58
63
59
64
```js
60
-
let clone =Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
This call makes a truly exact copy of `obj`, including all properties: enumerable and non-enumerable, data properties and setters/getters -- everything, and with the right `[[Prototype]]`.
64
71
65
-
## Brief history
66
72
67
-
If we count all the ways to manage `[[Prototype]]`, there are a lot! Many ways to do the same thing!
73
+
## Brief history
68
74
69
-
Why?
75
+
There're so many ways to manage `[[Prototype]]`. How did that happen? Why?
70
76
71
77
That's for historical reasons.
72
78
73
-
- The `"prototype"` property of a constructor function has worked since very ancient times.
74
-
- Later, in the year 2012, `Object.create` appeared in the standard. It gave the ability to create objects with a given prototype, but did not provide the ability to get/set it. So browsers implemented the non-standard `__proto__` accessor that allowed the user to get/set a prototype at any time.
79
+
The prototypal inheritance was in the language since its dawn, but the ways to manage it evolved over time.
80
+
81
+
- The `prototype` property of a constructor function has worked since very ancient times. It's the oldest way to create objects with a given prototype.
82
+
- Later, in the year 2012, `Object.create` appeared in the standard. It gave the ability to create objects with a given prototype, but did not provide the ability to get/set it. Some browsers implemented the non-standard `__proto__` accessor that allowed the user to get/set a prototype at any time, to give more flexibility to developers.
75
83
- Later, in the year 2015, `Object.setPrototypeOf` and `Object.getPrototypeOf` were added to the standard, to perform the same functionality as `__proto__`. As `__proto__` was de-facto implemented everywhere, it was kind-of deprecated and made its way to the Annex B of the standard, that is: optional for non-browser environments.
84
+
- Later, in the year 2022, it was officially allowed to use `__proto__` in object literals `{...}` (moved out of Annex B), but not as a getter/setter `obj.__proto__` (still in Annex B).
85
+
86
+
Why was `__proto__` replaced by the functions `getPrototypeOf/setPrototypeOf`?
87
+
88
+
Why was `__proto__` partially rehabilitated and its usage allowed in `{...}`, but not as a getter/setter?
76
89
77
-
As of now we have all these ways at our disposal.
90
+
That's an interesting question, requiring us to understand why `__proto__` is bad.
78
91
79
-
Why was `__proto__` replaced by the functions `getPrototypeOf/setPrototypeOf`? That's an interesting question, requiring us to understand why `__proto__` is bad. Read on to get the answer.
92
+
And soon we'll get the answer.
80
93
81
94
```warn header="Don't change `[[Prototype]]` on existing objects if speed matters"
82
95
Technically, we can get/set `[[Prototype]]` at any time. But usually we only set it once at the object creation time and don't modify it anymore: `rabbit` inherits from `animal`, and that is not going to change.
@@ -101,25 +114,36 @@ obj[key] = "some value";
101
114
alert(obj[key]); // [object Object], not "some value"!
102
115
```
103
116
104
-
Here, if the user types in `__proto__`, the assignment is ignored!
117
+
Here, if the user types in `__proto__`, the assignment in line 4 is ignored!
105
118
106
-
That shouldn't surprise us. The `__proto__` property is special: it must be either an object or `null`. A string can not become a prototype.
119
+
That could surely be surprising for a non-developer, but pretty understandable for us. The `__proto__` property is special: it must be either an object or `null`. A string can not become a prototype. That's why an assignment a string to `__proto__` is ignored.
107
120
108
121
But we didn't *intend* to implement such behavior, right? We want to store key/value pairs, and the key named `"__proto__"` was not properly saved. So that's a bug!
109
122
110
-
Here the consequences are not terrible. But in other cases we may be assigning object values, and then the prototype may indeed be changed. As a result, the execution will go wrong in totally unexpected ways.
123
+
Here the consequences are not terrible. But in other cases we may be storing objects instead of strings in `obj`, and then the prototype will indeed be changed. As a result, the execution will go wrong in totally unexpected ways.
111
124
112
125
What's worse -- usually developers do not think about such possibility at all. That makes such bugs hard to notice and even turn them into vulnerabilities, especially when JavaScript is used on server-side.
113
126
114
-
Unexpected things also may happen when assigning to `toString`, which is a function by default, and to other built-in methods.
127
+
Unexpected things also may happen when assigning to `obj.toString`, as it's a built-in object method.
115
128
116
129
How can we avoid this problem?
117
130
118
-
First, we can just switch to using `Map` for storage instead of plain objects, then everything's fine.
131
+
First, we can just switch to using `Map` for storage instead of plain objects, then everything's fine:
119
132
120
-
But `Object` can also serve us well here, because language creators gave thought to that problem long ago.
Modern methods to set up and directly access the prototype are:
177
-
178
-
-[Object.create(proto, [descriptors])](mdn:js/Object/create) -- creates an empty object with a given `proto` as `[[Prototype]]` (can be `null`) and optional property descriptors.
179
-
-[Object.getPrototypeOf(obj)](mdn:js/Object/getPrototypeOf) -- returns the `[[Prototype]]` of `obj` (same as `__proto__` getter).
180
-
-[Object.setPrototypeOf(obj, proto)](mdn:js/Object/setPrototypeOf) -- sets the `[[Prototype]]` of `obj` to `proto` (same as `__proto__` setter).
201
+
- To create an object with the given prototype, use:
181
202
182
-
The built-in `__proto__` getter/setter is unsafe if we'd want to put user-generated keys into an object. Just because a user may enter `"__proto__"` as the key, and there'll be an error, with hopefully light, but generally unpredictable consequences.
- or [Object.create(proto, [descriptors])](mdn:js/Object/create), allows to specify property descriptors.
183
205
184
-
So we can either use`Object.create(null)` to create a "very plain" object without `__proto__`, or stick to `Map` objects for that.
206
+
The`Object.create` provides an easy way to shallow-copy an object with all descriptors:
185
207
186
-
Also, `Object.create` provides an easy way to shallow-copy an object with all descriptors:
208
+
```js
209
+
let clone =Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
210
+
```
187
211
188
-
```js
189
-
let clone =Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
190
-
```
212
+
- Modern methods to get/set the prototype are:
191
213
192
-
We also made it clear that `__proto__` is a getter/setter for `[[Prototype]]` and resides in `Object.prototype`, just like other methods.
214
+
- [Object.getPrototypeOf(obj)](mdn:js/Object/getPrototypeOf) -- returns the `[[Prototype]]`of`obj` (same as `__proto__` getter).
215
+
- [Object.setPrototypeOf(obj, proto)](mdn:js/Object/setPrototypeOf) -- sets the `[[Prototype]]`of`obj` to `proto` (same as `__proto__` setter).
193
216
194
-
We can create an object without a prototype by `Object.create(null)`. Such objects are used as "pure dictionaries", they have no issues with `"__proto__"` as the key.
217
+
- Getting/setting the prototype using the built-in`__proto__` getter/setter isn't recommended, it's now in the Annex Bof the specification.
195
218
196
-
Other methods:
219
+
- We also covered prototype-less objects, created with`Object.create(null)` or `{__proto__: null}`.
197
220
198
-
-[Object.keys(obj)](mdn:js/Object/keys) / [Object.values(obj)](mdn:js/Object/values) / [Object.entries(obj)](mdn:js/Object/entries) -- returns an array of enumerable own string property names/values/key-value pairs.
199
-
-[Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) -- returns an array of all own symbolic keys.
200
-
-[Object.getOwnPropertyNames(obj)](mdn:js/Object/getOwnPropertyNames) -- returns an array of all own string keys.
201
-
-[Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) -- returns an array of all own keys.
202
-
-[obj.hasOwnProperty(key)](mdn:js/Object/hasOwnProperty): returns `true` if `obj` has its own (not inherited) key named `key`.
221
+
These objects are used as dictionaries, to store any (possibly user-generated) keys.
203
222
204
-
All methods that return object properties (like `Object.keys` and others) -- return "own" properties. If we want inherited ones, we can use `for..in`.
223
+
Normally, objects inherit built-in methods and `__proto__` getter/setter from `Object.prototype`, making corresponding keys "occupied" and potentially causing side effects. With`null` prototype, objects are truly empty.
0 commit comments