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
We can use `constructor` to create a new object using the same constructor as the existing one.
92
+
We can use `constructor`property to create a new object using the same constructor as the existing one.
93
93
94
94
Like here:
95
95
@@ -101,20 +101,36 @@ function Rabbit(name) {
101
101
102
102
let rabbit =newRabbit("White Rabbit");
103
103
104
+
*!*
104
105
let rabbit2 =newrabbit.constructor("Black Rabbit");
106
+
*/!*
105
107
```
106
108
107
-
That's handy when we have an object, don't know which constructor was used for it (e.g. it comes from a 3rd party library), and we need to create the same.
109
+
That's handy when we have an object, don't know which constructor was used for it (e.g. it comes from a 3rd party library), and we need to create another one of the same kind.
108
110
109
-
...But probably the most important thing about `"constructor"` is that...
111
+
But probably the most important thing about `"constructor"` is that...
110
112
111
-
**JavaScript itself does not ensure the right `"constructor"`at all.**
113
+
**...JavaScript itself does not ensure the right `"constructor"`value.**
112
114
113
-
Yes, it exists in the default `"prototype"` for functions, but that's all. It is created automatically, but what happens with it later -- is totally on us.
115
+
Yes, it exists in the default `"prototype"` for functions, but that's all. What happens with it later -- is totally on us.
114
116
115
-
In particular, if we replace the default prototype by assigning our own `Rabbit.prototype = { jumps: true }`, then there will be no `"constructor"` in it.
117
+
In particular, if we replace the default prototype as a whole, then there will be no `"constructor"` in it.
116
118
117
-
But we may want to keep `"constructor"` for convenience by adding properties to the default `"prototype"` instead of overwriting it as a whole:
119
+
For instance:
120
+
121
+
```js run
122
+
functionRabbit() {}
123
+
Rabbit.prototype= {
124
+
jumps:true
125
+
};
126
+
127
+
let rabbit =newRabbit();
128
+
*!*
129
+
alert(rabbit.constructor=== Rabbit); // false
130
+
*/!*
131
+
```
132
+
133
+
So, to keep the right `"constructor"` we can choose to add/remove properties to the default `"prototype"` instead of overwriting it as a whole:
118
134
119
135
```js
120
136
functionRabbit() {}
@@ -125,7 +141,7 @@ Rabbit.prototype.jumps = true
125
141
// the default Rabbit.prototype.constructor is preserved
126
142
```
127
143
128
-
Or, alternatively, recreate it manually:
144
+
Or, alternatively, recreate the `constructor` property it manually:
129
145
130
146
```js
131
147
Rabbit.prototype= {
@@ -134,6 +150,8 @@ Rabbit.prototype = {
134
150
constructor: Rabbit
135
151
*/!*
136
152
};
153
+
154
+
// now constructor is also correct, because we added it
137
155
```
138
156
139
157
@@ -143,17 +161,16 @@ In this chapter we briefly described the way of setting a `[[Prototype]]` for ob
143
161
144
162
Everything is quite simple, just few notes to make things clear:
145
163
146
-
- The `F.prototype` property is not the same as `[[Prototype]]`.
147
-
- The only thing `F.prototype` does: it sets `[[Prototype]]` of new objects when `new F()` is called.
164
+
- The `F.prototype` property is not the same as `[[Prototype]]`. The only thing `F.prototype` does: it sets `[[Prototype]]` of new objects when `new F()` is called.
148
165
- The value of `F.prototype` should be either an object or null: other values won't work.
149
-
- The `"prototype"` property only has such a special effect when is set to a constructor function, and it is invoked with `new`.
166
+
- The `"prototype"` property only has such a special effect when is set to a constructor function, and invoked with `new`.
150
167
151
-
On regular objects this property does nothing. That's an ordinary property:
168
+
On regular objects the `prototype` is nothing special:
152
169
```js
153
170
let user = {
154
171
name:"John",
155
172
prototype:"Bla-bla"// no magic at all
156
173
};
157
174
```
158
175
159
-
By default all functions have `F.prototype= { constructor: F }`. So by default we can get the constructor of an object by accessing its `"constructor"` property.
176
+
By default all functions have `F.prototype= { constructor: F }`, so we can get the constructor of an object by accessing its `"constructor"` property.
Copy file name to clipboardExpand all lines: 1-js/07-object-oriented-programming/06-prototype-methods/2-dictionary-tostring/task.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,7 +4,7 @@ importance: 5
4
4
5
5
# Add toString to the dictionary
6
6
7
-
There's an object `dictionary`, suited to store any `key/value` pairs.
7
+
There's an object `dictionary`, created as `Object.create(null)`, to store any `key/value` pairs.
8
8
9
9
Add method `dictionary.toString()` into it, that should return a comma-delimited list of keys. Your `toString` should not show up in `for..in` over the object.
Copy file name to clipboardExpand all lines: 1-js/07-object-oriented-programming/06-prototype-methods/article.md
+49-24Lines changed: 49 additions & 24 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,8 @@
1
1
2
2
# Methods for prototypes
3
3
4
+
In this chapter we cover additional methods to work with the prototype.
5
+
4
6
There are also other ways to get/set a prototype, besides those that we already know:
5
7
6
8
-[Object.create(proto[, descriptors])](mdn:js/Object/create) -- creates an empty object with given `proto` as `[[Prototype]]` and optional property descriptors.
alert(Object.getPrototypeOf(rabbit) === animal); // get the prototype of rabbit
29
+
*/!*
30
+
31
+
*!*
32
+
Object.setPrototypeOf(rabbit, {}); // change the prototype of rabbit to {}
33
+
*/!*
34
+
```
35
+
36
+
`Object.create` has an optional second argument: property descriptors. We can provide additional properties to the new object there, like this:
37
+
38
+
```js run
39
+
let animal = {
40
+
eats:true
41
+
};
42
+
43
+
let rabbit =Object.create(animal, {
44
+
jumps: {
45
+
value:true
46
+
}
47
+
});
25
48
26
-
Object.setPrototypeOf(rabbit, {}); //reset the prototype of rabbit to {}
49
+
alert(rabbit.jumps); //true
27
50
```
28
51
52
+
The descriptors are in the same format as described in the chapter <info:property-descriptors>.
29
53
30
-
````smart header="`Object.create` to make a clone"
31
-
`Object.create` can be used to perform a full object cloning:
54
+
We can use `Object.create` to perform a full object cloning, like this:
32
55
33
56
```js
34
57
// fully identical shallow clone of obj
35
58
let clone =Object.create(obj, Object.getOwnPropertyDescriptors(obj));
36
59
```
37
60
38
61
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]]`. But not an in-depth copy of course.
39
-
````
40
62
63
+
## Brief history
41
64
42
65
If we count all the ways to manage `[[Prototype]]`, there's a lot! Many ways to do the same!
43
66
@@ -46,18 +69,18 @@ Why so?
46
69
That's for historical reasons.
47
70
48
71
- The `"prototype"` property of a constructor function works since very ancient times.
49
-
- Later in the year 2012: `Object.create` appeared in the standard. It allowed to create objects with the given prototype, but that was all. So browsers implemented a more powerful, but non-standard `__proto__` accessor that allowed to get/set a prototype at any time.
50
-
- Later in the year 2015: `Object.setPrototypeOf` and `Object.getPrototypeOf` were added to the standard, and also `__proto__` became a part of the Annex B of the standard (optional for non-browser environments), because almost all browsers implemented it.
72
+
- Later in the year 2012: `Object.create` appeared in the standard. It allowed to create objects with the given prototype, but did not allow to get/set it. So browsers implemented non-standard `__proto__` accessor that allowed to get/set a prototype at any time.
73
+
- Later in the year 2015: `Object.setPrototypeOf` and `Object.getPrototypeOf` were added to the standard. The `__proto__`was de-facto implemented everywhere, so it made its way to the Annex B of the standard, that is optional for non-browser environments.
51
74
52
-
And now we have all these ways at our disposal.
75
+
As of now we have all these ways at our disposal.
53
76
54
-
But please note: for most practical tasks, prototype chains are fixed: `rabbit` inherits from `animal`, and that is not going to change. And JavaScript engines are highly optimized to that. Changing a prototype "on-the-fly" with `Object.setPrototypeOf` or `obj.__proto__=` is a very slow operation. But it is possible.
77
+
Technically, we can get/set `[[Prototype]]` at any time. But usually we only set it once at the object creation time, and then do not modify: `rabbit` inherits from `animal`, and that is not going to change. And JavaScript engines are highly optimized to that. Changing a prototype "on-the-fly" with `Object.setPrototypeOf` or `obj.__proto__=` is a very slow operation. But it is possible.
55
78
56
79
## "Very plain" objects
57
80
58
81
As we know, objects can be used as associative arrays to store key/value pairs.
59
82
60
-
...But if we try to use it for *any* keys (user-provided for instance), we can see an interesting glitch.
83
+
...But if we try to store *user-provided* keys in it (for instance, a user-entered dictionary), we can see an interesting glitch: all keys work fine except `"__proto__"`.
61
84
62
85
Check out the example:
63
86
@@ -70,15 +93,17 @@ obj[key] = "some value";
70
93
alert(obj[key]); // [object Object], not "some value"!
71
94
```
72
95
73
-
Here if the user types in `__proto__`, the assignment is ignored! That's because `__proto__` must be either an object or `null`, a string can not become a prototype.
96
+
Here if the user types in `__proto__`, the assignment is ignored!
74
97
75
-
We did not intend to implement such behavior, right? So that's a bug. Here the consequences are not terrible. But in more complex cases the prototype may indeed be changed, so the execution may go wrong in totally unexpected ways.
98
+
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.
99
+
100
+
But we did not 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. Here the consequences are not terrible. But in other cases the prototype may indeed be changed, so the execution may go wrong in totally unexpected ways.
76
101
77
102
What's worst -- 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.
78
103
79
104
Such thing happens only with `__proto__`. All other properties are "assignable" normally.
80
105
81
-
So, what's going and and how to evade the problem?
106
+
How to evade the problem?
82
107
83
108
First, we can just switch to using `Map`, then everything's fine.
84
109
@@ -92,7 +117,7 @@ So, if `obj.__proto__` is read or assigned, the corresponding getter/setter is c
92
117
93
118
As it was said in the beginning: `__proto__` is a way to access `[[Prototype]]`, it is not `[[Prototype]]` itself.
94
119
95
-
Now, if we want to use an object as an assotiative array, we can do it with a little trick:
120
+
Now, if we want to use an object as an associative array, we can do it with a little trick:
96
121
97
122
```js run
98
123
*!*
@@ -123,7 +148,7 @@ let obj = Object.create(null);
123
148
alert(obj); // Error (no toString)
124
149
```
125
150
126
-
...But that's usually fine for associative arrays. If needed, we can add a `toString` of our own.
151
+
...But that's usually fine for associative arrays.
127
152
128
153
Please note that most object-related methods are `Object.something(...)`, like `Object.keys(obj)` -- they are not in the prototype, so they will keep working on such objects:
129
154
@@ -142,9 +167,7 @@ There are many ways to get keys/values from an object.
142
167
143
168
We already know these ones:
144
169
145
-
- [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.
146
-
147
-
These methods only list *enumerable* properties, and those that have *strings as keys*.
170
+
-[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. These methods only list *enumerable* properties, and those that have *strings as keys*.
148
171
149
172
If we want symbolic properties:
150
173
@@ -154,13 +177,13 @@ If we want non-enumerable properties:
154
177
155
178
-[Object.getOwnPropertyNames(obj)](mdn:js/Object/getOwnPropertyNames) -- returns an array of all own string property names.
156
179
157
-
If we want *everything*:
180
+
If we want *all* properties:
158
181
159
182
-[Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) -- returns an array of all own property names.
160
183
161
-
All of them operate on the object itself. Properties from the prototype are not listed.
184
+
These methods are a bit different about which properties they return, but all of them operate on the object itself. Properties from the prototype are not listed.
162
185
163
-
But `for..in` loop is different: it also gives inherited properties.
186
+
The`for..in` loop is different: it loops over inherited properties too.
164
187
165
188
For instance:
166
189
@@ -208,9 +231,9 @@ Here we have the following inheritance chain: `rabbit`, then `animal`, then `Obj
208
231
209
232

210
233
211
-
So when `rabbit.hasOwnProperty` is called, the method `hasOwnProperty` is taken from `Object.prototype`. Why `hasOwnProperty` itself does not appear in `for..in` loop? The answer is simple: it's not enumerable just like all other properties of `Object.prototype`.
234
+
Note, there's one funny thing. Where is the method `rabbit.hasOwnProperty`coming from? Looking at the chain we can see that the method is provided by `Object.prototype.hasOwnProperty`. In other words, it's inherited.
212
235
213
-
As a take-away, let's remember that if we want inherited properties -- we should use `for..in`, and otherwise we can use other iteration methods or add the `hasOwnProperty` check.
236
+
...But why `hasOwnProperty` does not appear in `for..in` loop, if it lists all inherited properties? The answer is simple: it's not enumerable. Just like all other properties of `Object.prototype`. That's why they are not listed.
214
237
215
238
## Summary
216
239
@@ -227,4 +250,6 @@ Here's a brief list of methods we discussed in this chapter -- as a recap:
227
250
228
251
We also made it clear that `__proto__` is a getter/setter for `[[Prototype]]` and resides in `Object.prototype`, just as other methods.
229
252
230
-
`Object.create(null)` doesn't have any object properties and methods, and `__proto__` also doesn't exist for it.
253
+
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.
254
+
255
+
All methods that return object properties (like `Object.keys` and others) -- return "own" properties. If we want inherited ones, then we can use `for..in`.
0 commit comments