Skip to content

Commit b1f0cfc

Browse files
committed
up
1 parent b0976b5 commit b1f0cfc

File tree

3 files changed

+79
-54
lines changed

3 files changed

+79
-54
lines changed

1-js/9-object-inheritance/03-prototype/article.md

+12-27
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,20 @@ In programming, we often want to take something and extend it.
44

55
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.
66

7-
*Inheritance* is a language feature that helps in that.
7+
*Prototypal inheritance* is a language feature that helps in that.
88

99

1010
[cut]
1111

1212
## [[Prototype]]
1313

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":
1515

1616
![prototype](object-prototype-empty.png)
1717

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.
1919

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.
2121

2222
One of them is to use `__proto__`, like this:
2323

@@ -34,7 +34,7 @@ rabbit.__proto__ = animal;
3434
*/!*
3535
```
3636

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.
3838

3939
So now if we look for something in `rabbit` and it's missing, Javascript automatically takes it from `animal`.
4040

@@ -49,7 +49,7 @@ let rabbit = {
4949
};
5050

5151
*!*
52-
rabbit.__proto__ = animal;
52+
rabbit.__proto__ = animal; // (*)
5353
*/!*
5454

5555
// we can find both properties in rabbit now:
@@ -59,31 +59,16 @@ alert( rabbit.eats ); // true
5959
alert( rabbit.jumps ); // true
6060
```
6161

62-
Here `__proto__` sets `animal` to be a prototype of `rabbit`.
62+
Here the line `(*)` sets `animal` to be a prototype of `rabbit`.
6363

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):
6565

6666
![](proto-animal-rabbit.png)
6767

6868
Here we can say that "`animal` is the prototype of `rabbit`" or "`rabbit` prototypally inherits from `animal`".
6969

7070
So if `animal` has a lot of useful properties and methods, then they become automatically available in `rabbit`. Such properties are called "inherited".
7171

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-
8772
If we have a method in `animal`, it can be called on `rabbit`:
8873

8974
```js run
@@ -175,7 +160,7 @@ Since now, `rabbit.walk()` call finds the method immediately in the object and e
175160

176161
![](proto-animal-rabbit-walk-2.png)
177162

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:
179164

180165
```js run
181166
let user = {
@@ -203,16 +188,16 @@ alert(admin.name); // Alice
203188
alert(admin.surname); // Cooper
204189
```
205190

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.
207192

208193
So, the general rule would be:
209194

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).
211196
2. Otherwise assign directly to the object.
212197

213198
## The value of "this"
214199

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`?
216201

217202
The answer is simple: `this` is not affected by prototypes at all.
218203

1-js/9-object-inheritance/04-function-prototype/article.md

+19-11
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
# F.prototype
22

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.
44

55
[cut]
66

77
JavaScript has had prototypal inheritance from the beginning. It was one of the core features of the language.
88

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.
1010

1111
## The "prototype" property
1212

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.
1414

1515
That property is used by the Javascript itself to set `[[Prototype]]` for new objects.
1616

1717
**When a new object is created with `new F()`, the `[[Prototype]]` of it is set to `F.prototype`.**
1818

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.
2020

2121
Here's the example:
2222

@@ -44,15 +44,23 @@ That's the resulting picture:
4444

4545
![](proto-constructor-animal-rabbit.png)
4646

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`.
4848

4949
## Summary
5050

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.
5252

53-
Everything is quite simple, just few notes to make it clear:
53+
Everything is quite simple, just few notes to make things clear:
5454

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:
61+
```js
62+
let user = {
63+
name: "John",
64+
prototype: "Bla-bla" // no magic at all
65+
};
66+
```

1-js/9-object-inheritance/05-object-prototype/article.md

+48-16
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
# Object prototype and methods
1+
# Object.prototype and methods
22

33
The `"prototype"` property is widely used by the core of Javascript itself. All built-in constructor functions use it.
44

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.
66

77
Let's say we output an empty object:
88

@@ -11,7 +11,7 @@ let obj = {};
1111
alert( obj ); // "[object Object]" ?
1212
```
1313

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!
1515

1616
...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.
1717

@@ -25,7 +25,22 @@ When `new Object()` is called (or a literal object `{...}` is created), the `[[P
2525

2626
Afterwards when `obj.toString()` is called -- the method is taken from `Object.prototype`.
2727

28-
## Loops, obj.hasOwnProperty
28+
We can check it like this:
29+
30+
```js run
31+
let obj = {};
32+
33+
alert(obj.__proto__ === Object.prototype); // true
34+
// obj.toString === obj.__proto__toString == Object.prototype.toString
35+
```
36+
37+
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
2944

3045
There are many ways to get keys/values from an object:
3146

@@ -48,15 +63,17 @@ let rabbit = {
4863
};
4964

5065
*!*
66+
// only own keys
5167
alert(Object.keys(rabbit)); // jumps
5268
*/!*
5369

5470
*!*
71+
// inherited keys too
5572
for(let prop in rabbit) alert(prop); // jumps, then eats
5673
*/!*
5774
```
5875

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`.
6077

6178
So we can filter out inherited properties (or do something else with them):
6279

@@ -75,11 +92,11 @@ for(let prop in rabbit) {
7592
alert(`${prop}: ${isOwn}`); // jumps:true, then eats:false
7693
}
7794
```
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:
7996

8097
![](rabbit-animal-object.png)
8198

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`.
83100

84101
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.
85102

@@ -128,13 +145,13 @@ Why so?
128145
129146
That's for historical reasons.
130147
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.
133150
- 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.
134151
135152
And now we have all these ways at our disposal.
136153
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.
138155
139156
## "Very plain" objects
140157
@@ -165,7 +182,7 @@ So, what's going and and how to evade the problem?
165182
166183
First, we can just switch to using `Map`, then everything's fine.
167184
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.
169186
170187
The `__proto__` is not a property of an object, but an accessor property of `Object.prototype`:
171188
@@ -194,9 +211,9 @@ alert(obj[key]); // "some value"
194211
195212
So, there is no inherited getter/setter for `__proto__`. Now it is processed as a regular data property, so the example above works right.
196213
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 `{...}`.
198215
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`:
200217
201218
```js run
202219
*!*
@@ -208,9 +225,24 @@ alert(obj); // Error (no toString)
208225
209226
...But that's usually fine for associative arrays. If needed, we can add a `toString` of our own.
210227
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:
229+
230+
231+
```js run
232+
let chineseDictionary = Object.create(null);
233+
chineseDictionary.hello = "ni hao";
234+
chineseDictionary.bye = "zai jian";
235+
236+
alert(Object.keys(chineseDictionary)); // hello,bye
237+
```
238+
239+
240+
## Summary
241+
242+
The `"prototype"` property of constructor functions is essential for Javascript built-in methods.
212243
244+
In this chapter we saw how it works for objects:
213245
214-
## Summary [todo]
246+
-
215247
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

Comments
 (0)