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/9-object-inheritance/03-prototype/article.md
+12-27
Original file line number
Diff line number
Diff line change
@@ -4,20 +4,20 @@ In programming, we often want to take something and extend it.
4
4
5
5
For instance, we have a `user` object with its properties and methods, and want to make `admin` and `guest` as slightly modified variants of it. We'd like to reuse what we have in `user`, not copy/reimplement its methods, just build a new object on top of it.
6
6
7
-
*Inheritance* is a language feature that helps in that.
7
+
*Prototypal inheritance* is a language feature that helps in that.
8
8
9
9
10
10
[cut]
11
11
12
12
## [[Prototype]]
13
13
14
-
In Javascript, object have a special hidden property `[[Prototype]]`, that is either `null` or references another object. That object is called "a prototype":
14
+
In Javascript, objects have a special hidden property `[[Prototype]]`, that is either `null` or references another object. That object is called "a prototype":
15
15
16
16

17
17
18
-
That `[[Prototype]]` has a "magical" meaning. When we look for a property in`object`, and it's missing, Javascript automatically takes it from the prototype. In programming, such thing is called a "prototypal inheritance". Many cool language features and approaches are based on it.
18
+
That `[[Prototype]]` has a "magical" meaning. When we want to read a property from`object`, and it's missing, Javascript automatically takes it from the prototype. In programming, such thing is called a "prototypal inheritance". Many cool language features and approaches are based on it.
19
19
20
-
The property `[[Prototype]]` is hidden, but there are many ways to set it.
20
+
The property `[[Prototype]]` is internal and hidden, but there are many ways to set it.
21
21
22
22
One of them is to use `__proto__`, like this:
23
23
@@ -34,7 +34,7 @@ rabbit.__proto__ = animal;
34
34
*/!*
35
35
```
36
36
37
-
Please note that `__proto__` is *not the same* as `[[Prototype]]`. That's a getter/setter for it. Later we'll talk more about other ways of setting it, but for the start it'll do just fine.
37
+
Please note that `__proto__` is *not the same* as `[[Prototype]]`. That's a getter/setter for it. We'll talk about other ways of setting it later, as for now `__proto__` will do just fine.
38
38
39
39
So now if we look for something in `rabbit` and it's missing, Javascript automatically takes it from `animal`.
40
40
@@ -49,7 +49,7 @@ let rabbit = {
49
49
};
50
50
51
51
*!*
52
-
rabbit.__proto__= animal;
52
+
rabbit.__proto__= animal;// (*)
53
53
*/!*
54
54
55
55
// we can find both properties in rabbit now:
@@ -59,31 +59,16 @@ alert( rabbit.eats ); // true
59
59
alert( rabbit.jumps ); // true
60
60
```
61
61
62
-
Here `__proto__` sets `animal` to be a prototype of `rabbit`.
62
+
Here the line `(*)` sets `animal` to be a prototype of `rabbit`.
63
63
64
-
When`alert` tries to read property `rabbit.eats`, it can find it `rabbit`, so it follows the `[[Prototype]]` reference and finds it in `animal` (look from the bottom up):
64
+
Then, when`alert` tries to read property `rabbit.eats`, it can find it `rabbit`, so it follows the `[[Prototype]]` reference and finds it in `animal` (look from the bottom up):
65
65
66
66

67
67
68
68
Here we can say that "`animal` is the prototype of `rabbit`" or "`rabbit` prototypally inherits from `animal`".
69
69
70
70
So if `animal` has a lot of useful properties and methods, then they become automatically available in `rabbit`. Such properties are called "inherited".
71
71
72
-
Loops `for..in` also include inherited properties:
73
-
74
-
```js run
75
-
let animal = {
76
-
eats:true
77
-
};
78
-
79
-
let rabbit = {
80
-
jumps:true,
81
-
__proto__: animal
82
-
};
83
-
84
-
for(let prop in rabbit) alert(prop); // jumps, eats
85
-
```
86
-
87
72
If we have a method in `animal`, it can be called on `rabbit`:
88
73
89
74
```js run
@@ -175,7 +160,7 @@ Since now, `rabbit.walk()` call finds the method immediately in the object and e
175
160
176
161

177
162
178
-
The situation is a bit different for accessor properties: these properties behave more like functions. For instance, check out `admin.fullName` property in the code below:
163
+
The assignment handling is different for accessor properties, because these properties behave more like functions. For instance, check out `admin.fullName` property in the code below:
179
164
180
165
```js run
181
166
let user = {
@@ -203,16 +188,16 @@ alert(admin.name); // Alice
203
188
alert(admin.surname); // Cooper
204
189
```
205
190
206
-
Here the property `admin.fullName` has a setter in the prototype `user`. So it is not written into `admin`. Instead, the inherited setter is called.
191
+
Here in the line `(*)`the property `admin.fullName` has a setter in the prototype `user`. So it is not written into `admin`. Instead, the inherited setter is called.
207
192
208
193
So, the general rule would be:
209
194
210
-
1.If an assigned property has a setter in the prototype, then use it.
195
+
1.For accessor properties use a setter (from the prototype chain).
211
196
2. Otherwise assign directly to the object.
212
197
213
198
## The value of "this"
214
199
215
-
An interesting question may arise in the example above: what's the value of `this` inside `set fullName`? Where the properties `this.name` and `this.surname` are written: `user` or `admin`?
200
+
An interesting question may arise in the example above: what's the value of `this` inside `set fullName(value)`? Where the properties `this.name` and `this.surname` are written: `user` or `admin`?
216
201
217
202
The answer is simple: `this` is not affected by prototypes at all.
Copy file name to clipboardExpand all lines: 1-js/9-object-inheritance/04-function-prototype/article.md
+19-11
Original file line number
Diff line number
Diff line change
@@ -1,22 +1,22 @@
1
1
# F.prototype
2
2
3
-
In modern Javascript there are many ways to manipulate object prototype. But it wasn't like that all the time.
3
+
In modern Javascript we can set a prototype using `__proto__`. But it wasn't like that all the time.
4
4
5
5
[cut]
6
6
7
7
JavaScript has had prototypal inheritance from the beginning. It was one of the core features of the language.
8
8
9
-
But in the old times, there was no `__proto__`. There was another (and the only) way to set it: to use a `"prototype"` property of the constructor function. And there are still many scripts that use it.
9
+
But in the old times, there was another (and the only) way to set it: to use a `"prototype"` property of the constructor function. And there are still many scripts that use it.
10
10
11
11
## The "prototype" property
12
12
13
-
As we know already, `new F()` creates a new object. But what we didn't use yet is a `"prototype"` property on it.
13
+
As we know already, `new F()` creates a new object. But what we didn't use yet `F.prototype` property.
14
14
15
15
That property is used by the Javascript itself to set `[[Prototype]]` for new objects.
16
16
17
17
**When a new object is created with `new F()`, the `[[Prototype]]` of it is set to `F.prototype`.**
18
18
19
-
Please note that `F.prototype` here means a regular property named `"prototype"`. It sounds something similar to the term "prototype", but here we really mean a regular property with this name.
19
+
Please note that `F.prototype` here means a regular property named `"prototype"` on `F`. It sounds something similar to the term "prototype", but here we really mean a regular property with this name.
20
20
21
21
Here's the example:
22
22
@@ -44,15 +44,23 @@ That's the resulting picture:
44
44
45
45

46
46
47
-
On the picture, `"prototype"` is a horizontal arrow, meaning that it's a regular property, and `[[Prototype]]` is vertical, meaning the inheritance of `rabbit` from `animal`.
47
+
On the picture, `"prototype"` is a horizontal arrow, it's a regular property, and `[[Prototype]]` is vertical, meaning the inheritance of `rabbit` from `animal`.
48
48
49
49
## Summary
50
50
51
-
In this chapter we briefly described the way of setting a `[[Prototype]]` for objects created via a constructor function. There are many scripts that rely on it.
51
+
In this chapter we briefly described the way of setting a `[[Prototype]]` for objects created via a constructor function. Later we'll see more advanced programming patterns that rely on it.
52
52
53
-
Everything is quite simple, just few notes to make it clear:
53
+
Everything is quite simple, just few notes to make things clear:
54
54
55
-
- The `"prototype"` property is not `[[Prototype]]`. Here in the text there are quotes around `"prototype"` to emphasize it.
56
-
- The only thing `"F.prototype"` does: it sets `[[Prototype]]` of new objects when `new F()` is called.
57
-
- The value of `"prototype"` should be either an object or null: other values won't work.
58
-
- The `"prototype"` property only has such a special effect when is set to a constructor function, and it is invoked with `new`. On regular objects this property does nothing.
55
+
- The `F.prototype` property is not the same as `[[Prototype]]`.
56
+
- The only thing `F.prototype` does: it sets `[[Prototype]]` of new objects when `new F()` is called.
57
+
- The value of `F.prototype` should be either an object or null: other values won't work.
58
+
- The `"prototype"` property only has such a special effect when is set to a constructor function, and it is invoked with `new`.
59
+
60
+
On regular objects this property does nothing. That's an ordinary property:
Copy file name to clipboardExpand all lines: 1-js/9-object-inheritance/05-object-prototype/article.md
+48-16
Original file line number
Diff line number
Diff line change
@@ -1,8 +1,8 @@
1
-
# Objectprototype and methods
1
+
# Object.prototype and methods
2
2
3
3
The `"prototype"` property is widely used by the core of Javascript itself. All built-in constructor functions use it.
4
4
5
-
We'll see how it works for plain objects first, and then for more complex ones.
5
+
We'll see how it is for plain objects first, and then for more complex ones.
6
6
7
7
Let's say we output an empty object:
8
8
@@ -11,7 +11,7 @@ let obj = {};
11
11
alert( obj ); // "[object Object]" ?
12
12
```
13
13
14
-
Where's the code that generates `"[object Object]"`? That's a built-in `toString` method, but where is it? The object is empty!
14
+
Where's the code that generates the string `"[object Object]"`? That's a built-in `toString` method, but where is it? The `obj` is empty!
15
15
16
16
...But the short notation `obj = {}` is the same as `obj = new Object()`, where `Object` -- is a built-in object constructor function. And that function has `Object.prototype` that references a huge object with `toString` and other functions.
17
17
@@ -25,7 +25,22 @@ When `new Object()` is called (or a literal object `{...}` is created), the `[[P
25
25
26
26
Afterwards when `obj.toString()` is called -- the method is taken from `Object.prototype`.
Please note that there is no additional `[[Prototype]]` in the chain above `Object.prototype`:
38
+
39
+
```js run
40
+
alert(Object.prototype.__proto__); // null
41
+
```
42
+
43
+
## Getting all properties
29
44
30
45
There are many ways to get keys/values from an object:
31
46
@@ -48,15 +63,17 @@ let rabbit = {
48
63
};
49
64
50
65
*!*
66
+
// only own keys
51
67
alert(Object.keys(rabbit)); // jumps
52
68
*/!*
53
69
54
70
*!*
71
+
// inherited keys too
55
72
for(let prop in rabbit) alert(prop); // jumps, then eats
56
73
*/!*
57
74
```
58
75
59
-
If we insist on using `for..in` for looping, there's a built-in method [obj.hasOwnProperty(key)](mdn:js/Object/hasOwnProperty): it returns `true` if `obj` has its own (not inherited) property named `key`.
76
+
If we want to differ inherited properties, there's a built-in method [obj.hasOwnProperty(key)](mdn:js/Object/hasOwnProperty): it returns `true` if `obj` has its own (not inherited) property named `key`.
60
77
61
78
So we can filter out inherited properties (or do something else with them):
62
79
@@ -75,11 +92,11 @@ for(let prop in rabbit) {
75
92
alert(`${prop}: ${isOwn}`); // jumps:true, then eats:false
76
93
}
77
94
```
78
-
Here we have the following inheritance chain: `rabbit`, then `animal`, then `Object.prototype`, the implicit prototype of `animal`, and only`null` above it:
95
+
Here we have the following inheritance chain: `rabbit`, then `animal`, then `Object.prototype` (because `animal` is a literal object `{...}`, so it's by default), and then`null` above it:
79
96
80
97

81
98
82
-
So when `rabbit.hasOwnProperty` is called, the method `hasOwnProperty` is taken from `Object.prototype`. Why `hasOwnProperty` itself does not appear in `for..in` loop? That's simple: it's not enumerable.
99
+
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`.
83
100
84
101
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.
85
102
@@ -128,13 +145,13 @@ Why so?
128
145
129
146
That's for historical reasons.
130
147
131
-
- The `"prototype"` property of constructor functions works since very ancient times.
132
-
- Later in the year 2012: `Object.create` appeared in the standard. But it only allowed to create objects with the given prototype. So browsers implemented a more powerful, but non-standard `__proto__` accessor that allowed to get/set prototype at any time.
148
+
- The `"prototype"` property of a constructor function works since very ancient times.
149
+
- 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.
133
150
- 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.
134
151
135
152
And now we have all these ways at our disposal.
136
153
137
-
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.
154
+
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.
138
155
139
156
## "Very plain" objects
140
157
@@ -165,7 +182,7 @@ So, what's going and and how to evade the problem?
165
182
166
183
First, we can just switch to using `Map`, then everything's fine.
167
184
168
-
But `Object` also can server well here, because language creators gave a thought to that problem long ago.
185
+
But `Object` also can serve us well here, because language creators gave a thought to that problem long ago.
169
186
170
187
The `__proto__` is not a property of an object, but an accessor property of `Object.prototype`:
So, there is no inherited getter/setter for `__proto__`. Now it is processed as a regular data property, so the example above works right.
196
213
197
-
We can call such object "very plain", because they are even simpler than regular plain object `{...}`.
214
+
We can call such object "very plain" or "pure dictionary objects", because they are even simpler than regular plain object `{...}`.
198
215
199
-
A downside is that such objects lack all built-in object methods, e.g. `toString`:
216
+
A downside is that such objects lack any built-in object methods, e.g. `toString`:
200
217
201
218
```js run
202
219
*!*
@@ -208,9 +225,24 @@ alert(obj); // Error (no toString)
208
225
209
226
...But that's usually fine for associative arrays. If needed, we can add a `toString` of our own.
210
227
211
-
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.
228
+
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:
The `"prototype"` property of constructor functions is essential for Javascript built-in methods.
212
243
244
+
In this chapter we saw how it works for objects:
213
245
214
-
## Summary [todo]
246
+
-
215
247
216
-
Here in the tutorial I use `__proto__` for shorter and more readable examples. Also all modern engines support it. But in the long run, `Object.getPrototypeOf/setPrototypeOf` is safer.
248
+
In the next chapter we'll see the bigger picture: how other built-ins rely on it to inherit from each other.
0 commit comments