Skip to content

Commit dc7bf0b

Browse files
authored
Merge pull request #216 from vplentinax/sync-protIn
Sincronización 08-prototypes
2 parents b9f8073 + 7a15545 commit dc7bf0b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+201
-1143
lines changed

‎1-js/08-prototypes/01-prototype-inheritance/2-search-algorithm/task.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ importance: 5
66

77
The task has two parts.
88

9-
We have an object:
9+
Given the following objects:
1010

1111
```js
1212
let head ={

‎1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/solution.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
That's because `this` is an object before the dot, so `rabbit.eat()` modifies `rabbit`.
44

55
Property lookup and execution are two different things.
6-
The method `rabbit.eat` is first found in the prototype, then executed with `this=rabbit`
6+
7+
The method `rabbit.eat` is first found in the prototype, then executed with `this=rabbit`.

‎1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/task.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ importance: 5
22

33
---
44

5-
# Where it writes?
5+
# Where does it write?
66

77
We have `rabbit` inheriting from `animal`.
88

‎1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/solution.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Let's look carefully at what's going on in the call `speedy.eat("apple")`.
1010

1111
So all hamsters share a single stomach!
1212

13-
Every time the `stomach` is taken from the prototype, then `stomach.push` modifies it "at place".
13+
Both for `lazy.stomach.push(...)` and `speedy.stomach.push()`, the property `stomach` is found in the prototype (as it's not in the object itself), then the new data is pushed into it.
1414

1515
Please note that such thing doesn't happen in case of a simple assignment `this.stomach=`:
1616

@@ -44,7 +44,7 @@ alert( lazy.stomach ); // <nothing>
4444

4545
Now all works fine, because `this.stomach=` does not perform a lookup of `stomach`. The value is written directly into `this` object.
4646

47-
Also we can totally evade the problem by making sure that each hamster has their own stomach:
47+
Also we can totally avoid the problem by making sure that each hamster has their own stomach:
4848

4949
```js run
5050
let hamster ={
@@ -77,4 +77,4 @@ alert( speedy.stomach ); // apple
7777
alert( lazy.stomach ); // <nothing>
7878
```
7979

80-
As a common solution, all properties that describe the state of a particular object, like `stomach` above, are usually written into that object. That prevents such problems.
80+
As a common solution, all properties that describe the state of a particular object, like `stomach` above, should be written into that object. That prevents such problems.

‎1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/task.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ importance: 5
22

33
---
44

5-
# Why two hamsters are full?
5+
# Why are both hamsters full?
66

77
We have two hamsters: `speedy` and `lazy` inheriting from the general `hamster` object.
88

9-
When we feed one of them, the other one is also full. Why? How to fix it?
9+
When we feed one of them, the other one is also full. Why? How can we fix it?
1010

1111
```js run
1212
let hamster ={

‎1-js/08-prototypes/01-prototype-inheritance/article.md‎

Lines changed: 82 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ In JavaScript, objects have a special hidden property `[[Prototype]]` (as named
1212

1313
![prototype](object-prototype-empty.svg)
1414

15-
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 "prototypal inheritance". Many cool language features and programming techniques are based on it.
15+
The prototype is a little bit "magical". 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 "prototypal inheritance". Many cool language features and programming techniques are based on it.
1616

1717
The property `[[Prototype]]` is internal and hidden, but there are many ways to set it.
1818

19-
One of them is to use `__proto__`, like this:
19+
One of them is to use the special name `__proto__`, like this:
2020

2121
```js run
2222
let animal ={
@@ -32,9 +32,9 @@ rabbit.__proto__ = animal;
3232
```
3333

3434
```smart header="`__proto__` is a historical getter/setter for `[[Prototype]]`"
35-
Please note that `__proto__` is *not the same* as `[[Prototype]]`. That's a getter/setter for it.
35+
Please note that `__proto__` is *not the same* as `[[Prototype]]`. It's a getter/setter for it.
3636

37-
It exists for historical reasons, in modern language it is replaced with functions `Object.getPrototypeOf/Object.setPrototypeOf` that also get/set the prototype. We'll study the reasons for that and these functions later.
37+
It exists for historical reasons. In modern language it is replaced with functions `Object.getPrototypeOf/Object.setPrototypeOf` that also get/set the prototype. We'll study the reasons for that and these functions later.
3838

3939
By the specification, `__proto__` must only be supported by browsers, but in fact all environments including server-side support it. For now, as `__proto__` notation is a little bit more intuitively obvious, we'll use it in the examples.
4040
```
@@ -43,7 +43,7 @@ If we look for a property in `rabbit`, and it's missing, JavaScript automaticall
4343
4444
For instance:
4545
46-
```js run
46+
```js
4747
let animal ={
4848
eats: true
4949
};
@@ -101,7 +101,6 @@ The method is automatically taken from the prototype, like this:
101101

102102
The prototype chain can be longer:
103103

104-
105104
```js run
106105
let animal ={
107106
eats:true,
@@ -131,10 +130,10 @@ alert(longEar.jumps); // true (from rabbit)
131130

132131
![](proto-animal-rabbit-chain.svg)
133132

134-
There are actually only two limitations:
133+
There are only two limitations:
135134

136135
1. The references can't go in circles. JavaScript will throw an error if we try to assign `__proto__` in a circle.
137-
2. The value of `__proto__` can be either an object or `null`, other types (like primitives) are ignored.
136+
2. The value of `__proto__` can be either an object or `null`. Other types are ignored.
138137

139138
Also it may be obvious, but still: there can be only one `[[Prototype]]`. An object may not inherit from two others.
140139

@@ -171,7 +170,7 @@ From now on, `rabbit.walk()` call finds the method immediately in the object and
171170

172171
![](proto-animal-rabbit-walk-2.svg)
173172

174-
That's for data properties only, not for accessors. If a property is a getter/setter, then it behaves like a function: getters/setters are looked up in the prototype.
173+
Accessor properties are an exception, as assignment is handled by a setter function. So writing to such a property is actually the same as calling a function.
175174

176175
For that reason `admin.fullName` works correctly in the code below:
177176

@@ -204,15 +203,15 @@ Here in the line `(*)` the property `admin.fullName` has a getter in the prototy
204203

205204
## The value of "this"
206205

207-
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: into `user` or `admin`?
206+
An interesting question may arise in the example above: what's the value of `this` inside `set fullName(value)`? Where are the properties `this.name` and `this.surname` written: into `user` or `admin`?
208207

209208
The answer is simple: `this` is not affected by prototypes at all.
210209

211210
**No matter where the method is found: in an object or its prototype. In a method call, `this` is always the object before the dot.**
212211

213212
So, the setter call `admin.fullName=` uses `admin` as `this`, not `user`.
214213

215-
That is actually a super-important thing, because we may have a big object with many methods and inherit from it. Then inherited objects can run its methods, and they will modify the state of these objects, not the big one.
214+
That is actually a super-important thing, because we may have a big object with many methods, and have objects that inherit from it. And when the inheriting objects run the inherited methods, they will modify only their own states, not the state of the big object.
216215

217216
For instance, here `animal` represents a "method storage", and `rabbit` makes use of it.
218217

@@ -247,14 +246,84 @@ The resulting picture:
247246

248247
![](proto-animal-rabbit-walk-3.svg)
249248

250-
If we had other objects like `bird`, `snake` etc inheriting from `animal`, they would also gain access to methods of `animal`. But `this` in each method would be the corresponding object, evaluated at the call-time (before dot), not `animal`. So when we write data into `this`, it is stored into these objects.
249+
If we had other objects, like `bird`, `snake`, etc., inheriting from `animal`, they would also gain access to methods of `animal`. But `this` in each method call would be the corresponding object, evaluated at the call-time (before dot), not `animal`. So when we write data into `this`, it is stored into these objects.
251250

252251
As a result, methods are shared, but the object state is not.
253252

253+
## for..in loop
254+
255+
The `for..in` loop iterates over inherited properties too.
256+
257+
For instance:
258+
259+
```js run
260+
let animal ={
261+
eats:true
262+
};
263+
264+
let rabbit ={
265+
jumps:true,
266+
__proto__: animal
267+
};
268+
269+
*!*
270+
// Object.keys only returns own keys
271+
alert(Object.keys(rabbit)); // jumps
272+
*/!*
273+
274+
*!*
275+
// for..in loops over both own and inherited keys
276+
for(let prop in rabbit) alert(prop); // jumps, then eats
277+
*/!*
278+
```
279+
280+
If that's not what we want, and we'd like to exclude 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`.
281+
282+
So we can filter out inherited properties (or do something else with them):
283+
284+
```js run
285+
let animal ={
286+
eats:true
287+
};
288+
289+
let rabbit ={
290+
jumps:true,
291+
__proto__: animal
292+
};
293+
294+
for(let prop in rabbit){
295+
let isOwn =rabbit.hasOwnProperty(prop);
296+
297+
if (isOwn){
298+
alert(`Our: ${prop}`); // Our: jumps
299+
} else{
300+
alert(`Inherited: ${prop}`); // Inherited: eats
301+
}
302+
}
303+
```
304+
305+
Here we have the following inheritance chain: `rabbit` inherits from `animal`, that inherits from `Object.prototype` (because `animal` is a literal object `{...}`, so it's by default), and then `null` above it:
306+
307+
![](rabbit-animal-object.svg)
308+
309+
Note, there's one funny thing. Where is the method `rabbit.hasOwnProperty` coming from? We did not define it. Looking at the chain we can see that the method is provided by `Object.prototype.hasOwnProperty`. In other words, it's inherited.
310+
311+
...But why does `hasOwnProperty` not appear in the `for..in` loop like `eats` and `jumps` do, if `for..in` lists inherited properties?
312+
313+
The answer is simple: it's not enumerable. Just like all other properties of `Object.prototype`, it has `enumerable:false` flag. And `for..in` only lists enumerable properties. That's why it and the rest of the `Object.prototype` properties are not listed.
314+
315+
```smart header="Almost all other key/value-getting methods ignore inherited properties"
316+
Almost all other key/value-getting methods, such as `Object.keys`, `Object.values` and so on ignore inherited properties.
317+
318+
They only operate on the object itself. Properties from the prototype are *not* taken into account.
319+
```
320+
254321
## Summary
255322

256323
- In JavaScript, all objects have a hidden `[[Prototype]]` property that's either another object or `null`.
257324
- We can use `obj.__proto__` to access it (a historical getter/setter, there are other ways, to be covered soon).
258325
- The object referenced by `[[Prototype]]` is called a "prototype".
259-
- If we want to read a property of `obj` or call a method, and it doesn't exist, then JavaScript tries to find it in the prototype. Write/delete operations work directly on the object, they don't use the prototype (unless the property is actually a setter).
326+
- If we want to read a property of `obj` or call a method, and it doesn't exist, then JavaScript tries to find it in the prototype.
327+
- Write/delete operations act directly on the object, they don't use the prototype (assuming it's a data property, not a setter).
260328
- If we call `obj.method()`, and the `method` is taken from the prototype, `this` still references `obj`. So methods always work with the current object even if they are inherited.
329+
- The `for..in` loop iterates over both its own and its inherited properties. All other key/value-getting methods only operate on the object itself.
Lines changed: 1 addition & 22 deletions
Loading
Lines changed: 1 addition & 40 deletions
Loading

0 commit comments

Comments
(0)