Skip to content

Commit df7800a

Browse files
committed
improvement
1 parent 9f7235d commit df7800a

File tree

6 files changed

+99
-22
lines changed

6 files changed

+99
-22
lines changed

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

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -268,12 +268,12 @@ let rabbit = {
268268
};
269269

270270
*!*
271-
// only own keys
271+
// Object.keys only return own keys
272272
alert(Object.keys(rabbit)); // jumps
273273
*/!*
274274

275275
*!*
276-
// inherited keys too
276+
// for..in loops over both own and inherited keys
277277
for(let prop in rabbit) alert(prop); // jumps, then eats
278278
*/!*
279279
```
@@ -294,17 +294,24 @@ let rabbit = {
294294

295295
for(let prop in rabbit) {
296296
let isOwn = rabbit.hasOwnProperty(prop);
297-
alert(`${prop}: ${isOwn}`); // jumps: true, then eats: false
297+
298+
if (isOwn) {
299+
alert(`Our: ${prop}`); // Our: jumps
300+
} else {
301+
alert(`Inherited: ${prop}`); // Inherited: eats
302+
}
298303
}
299304
```
300305

301-
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:
306+
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:
302307

303308
![](rabbit-animal-object.png)
304309

305-
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.
310+
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.
311+
312+
...But why `hasOwnProperty` does not appear in `for..in` loop, like `eats` and `jumps`, if it lists all inherited properties.
306313

307-
...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.
314+
The answer is simple: it's not enumerable. Just like all other properties of `Object.prototype`, it has `enumerable:false` flag. That's why they are not listed.
308315

309316
```smart header="All other iteration methods ignore inherited properties"
310317
All other key/value-getting methods, such as `Object.keys`, `Object.values` and so on ignore inherited properties.
@@ -317,6 +324,7 @@ They only operate on the object itself. Properties from the prototype are taken
317324
- In JavaScript, all objects have a hidden `[[Prototype]]` property that's either another object or `null`.
318325
- We can use `obj.__proto__` to access it (a historical getter/setter, there are other ways, to be covered soon).
319326
- The object referenced by `[[Prototype]]` is called a "prototype".
320-
- 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).
327+
- 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.
328+
- Write/delete operations for act directly on the object, they don't use the prototype (assuming it's a data property, not is a setter).
321329
- 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.
322330
- The `for..in` loop iterates over both own and inherited properties. All other key/value-getting methods only operate on the object itself.

1-js/09-classes/02-class-inheritance/article.md

Lines changed: 81 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -311,9 +311,9 @@ Let's get a little deeper under the hood of `super`. We'll see some interesting
311311

312312
First to say, from all that we've learned till now, it's impossible for `super` to work at all!
313313

314-
Yeah, indeed, let's ask ourselves, how it could technically work? When an object method runs, it gets the current object as `this`. If we call `super.method()` then, it needs to retrieve the `method` from the prototype of the current object. How JavaScript engine should get the prototype of `this`?
314+
Yeah, indeed, let's ask ourselves, how it could technically work? When an object method runs, it gets the current object as `this`. If we call `super.method()` then, it needs to retrieve the `method` from the prototype of the current object.
315315

316-
The task may seem simple, but it isn't. The engine could try to get the method from `[[Prototype]]` of `this`, as `this.__proto__.method`. Unfortunately, that doesn't work.
316+
The task may seem simple, but it isn't. The engine knows the current object `this`, so it could get the parent `method` as `this.__proto__.method`. Unfortunately, such a "naive" solution won't work.
317317

318318
Let's demonstrate the problem. Without classes, using plain objects for the sake of simplicity.
319319

@@ -414,46 +414,100 @@ The problem can't be solved by using `this` alone.
414414
415415
To provide the solution, JavaScript adds one more special internal property for functions: `[[HomeObject]]`.
416416
417-
**When a function is specified as a class or object method, its `[[HomeObject]]` property becomes that object.**
417+
When a function is specified as a class or object method, its `[[HomeObject]]` property becomes that object.
418418
419-
This actually violates the idea of "unbound" functions, because methods remember their objects. And `[[HomeObject]]` can't be changed, so this bound is forever. So that's a very important change in the language.
419+
Then `super` uses it to resolve the parent prototype and its methods.
420420
421-
But this change is safe. `[[HomeObject]]` is used only for calling parent methods in `super`, to resolve the prototype. So it doesn't break compatibility. Regular method calls know nothing about `[[HomeObject]]`, it only matters for `super`.
422-
423-
Let's see how it works for `super` -- again, using plain objects:
421+
Let's see how it works, first with plain objects:
424422

425423
```js run
426424
let animal = {
427425
name: "Animal",
428-
eat() { // [[HomeObject]] == animal
426+
eat() { // animal.eat.[[HomeObject]] == animal
429427
alert(`${this.name} eats.`);
430428
}
431429
};
432430
433431
let rabbit = {
434432
__proto__: animal,
435433
name: "Rabbit",
436-
eat() { // [[HomeObject]] == rabbit
434+
eat() { // rabbit.eat.[[HomeObject]] == rabbit
437435
super.eat();
438436
}
439437
};
440438
441439
let longEar = {
442440
__proto__: rabbit,
443441
name: "Long Ear",
444-
eat() { // [[HomeObject]] == longEar
442+
eat() { // longEar.eat.[[HomeObject]] == longEar
445443
super.eat();
446444
}
447445
};
448446
449447
*!*
448+
// works correctly
450449
longEar.eat(); // Long Ear eats.
451450
*/!*
452451
```
453452

454-
Every method remembers its object in the internal `[[HomeObject]]` property. Then `super` uses it to resolve the parent prototype.
453+
It works as intended, due to `[[HomeObject]]` mechanics. A method, such as `longEar.eat`, knows its `[[HomeObject]]` and takes the parent method from its prototype. Without any use of `this`.
454+
455+
### Methods are not "free"
456+
457+
As we've known before, generally functions are "free", not bound to objects in JavaScript. So they can be copied between objects and called with another `this`.
458+
459+
The very existance of `[[HomeObject]]` violates that principle, because methods remember their objects. `[[HomeObject]]` can't be changed, so this bond is forever.
460+
461+
The only place in the language where `[[HomeObject]]` is used -- is `super`. So, if a method does not use `super`, then we can still consider it free and copy between objects. But with `super` things may go wrong.
462+
463+
Here's the demo of a wrong `super` call:
464+
465+
```js run
466+
let animal = {
467+
sayHi() {
468+
console.log(`I'm an animal`);
469+
}
470+
};
471+
472+
let rabbit = {
473+
__proto__: animal,
474+
sayHi() {
475+
super.sayHi();
476+
}
477+
};
455478
456-
`[[HomeObject]]` is defined for methods defined both in classes and in plain objects. But for objects, methods must be specified exactly the given way: as `method()`, not as `"method: function()"`.
479+
let plant = {
480+
sayHi() {
481+
console.log("I'm a plant");
482+
}
483+
};
484+
485+
let tree = {
486+
__proto__: plant,
487+
*!*
488+
sayHi: rabbit.sayHi // (*)
489+
*/!*
490+
};
491+
492+
*!*
493+
tree.sayHi(); // I'm an animal (?!?)
494+
*/!*
495+
```
496+
497+
A call to `tree.sayHi()` shows "I'm an animal". Definitevely wrong.
498+
499+
The reason is simple:
500+
- In the line `(*)`, the method `tree.sayHi` was copied from `rabbit`. Maybe we just wanted to avoid code duplication?
501+
- So its `[[HomeObject]]` is `rabbit`, as it was created in `rabbit`. There's no way to change `[[HomeObject]]`.
502+
- The code of `tree.sayHi()` has `super.sayHi()` inside. It goes up from `rabbit` and takes the method from `animal`.
503+
504+
![](super-homeobject-wrong.png)
505+
506+
### Methods, not function properties
507+
508+
`[[HomeObject]]` is defined for methods both in classes and in plain objects. But for objects, methods must be specified exactly as `method()`, not as `"method: function()"`.
509+
510+
The difference may be non-essential for us, but it's important for JavaScript.
457511

458512
In the example below a non-method syntax is used for comparison. `[[HomeObject]]` property is not set and the inheritance doesn't work:
459513
@@ -475,3 +529,18 @@ let rabbit = {
475529
rabbit.eat(); // Error calling super (because there's no [[HomeObject]])
476530
*/!*
477531
```
532+
533+
## Summary
534+
535+
1. To extend a class: `class Child extends Parent`:
536+
- That means `Child.prototype.__proto__` will be `Parent.prototype`, so methods are inherited.
537+
2. When overriding a constructor:
538+
- We must call parent constructor as `super()` in `Child` constructor before using `this`.
539+
3. When overriding another method:
540+
- We can use `super.method()` in a `Child` method to call `Parent` method.
541+
4. Internals:
542+
- Methods remember their class/object in the internal `[[HomeObject]]` property. That's how `super` resolves parent methods.
543+
- So it's not safe to copy a method with `super` from one object to another.
544+
545+
Also:
546+
- Arrow functions don't have own `this` or `super`, so they transparently fit into the surrounding context.
10.5 KB
Loading

10-misc/12-mutation-observer/article.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ Then after any changes, the `callback` is executed, with a list of [MutationReco
3636

3737
- `type` -- mutation type, one of
3838
- `"attributes"`: attribute modified
39-
- `"characterData"`: data modified
40-
- `"childList"`: elements added/removed,
39+
- `"characterData"`: data modified, used for text nodes,
40+
- `"childList"`: child elements added/removed,
4141
- `target` -- where the change occured: an element for "attributes", or text node for "characterData", or an element for a "childList" mutation,
4242
- `addedNodes/removedNodes` -- nodes that were added/removed,
4343
- `previousSibling/nextSibling` -- the previous and next sibling to added/removed nodes,

2-ui/5-loading/01-onload-ondomcontentloaded/article.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
The lifecycle of an HTML page has three important events:
44

55
- `DOMContentLoaded` -- the browser fully loaded HTML, and the DOM tree is built, but external resources like pictures `<img>` and stylesheets may be not yet loaded.
6-
- `load` -- not onyl HTML is loaded, but also all the external resources: images, styles etc.
6+
- `load` -- not only HTML is loaded, but also all the external resources: images, styles etc.
77
- `beforeunload/unload` -- the user is leaving the page.
88

99
Each event may be useful:

figures.sketch

7.54 KB
Binary file not shown.

0 commit comments

Comments
 (0)