Skip to content

Commit 6d1fa5d

Browse files
committed
fixes
1 parent f968724 commit 6d1fa5d

File tree

8 files changed

+84
-79
lines changed

8 files changed

+84
-79
lines changed

Diff for: 1-js/09-classes/03-static-properties-methods/article.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,6 @@ MyClass.property = ...
224224
MyClass.method = ...
225225
```
226226

227-
Static properties are inherited.
227+
Static properties and methods are inherited.
228228

229229
For `class B extends A` the prototype of the class `B` itself points to `A`: `B.[[Prototype]] = A`. So if a field is not found in `B`, the search continues in `A`.

Diff for: 1-js/09-classes/04-private-protected-properties-methods/article.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ In JavaScript, there are two types of object fields (properties and methods):
5353
- Public: accessible from anywhere. They comprise the external interface. Till now we were only using public properties and methods.
5454
- Private: accessible only from inside the class. These are for the internal interface.
5555

56-
In many other languages there also exist "protected" fields: accessible only from inside the class and those extending it. They are also useful for the internal interface. They are in a sense more widespread than private ones, because we usually want inheriting classes to gain access to them.
56+
In many other languages there also exist "protected" fields: accessible only from inside the class and those extending it (like private, but plus access from inheriting classes). They are also useful for the internal interface. They are in a sense more widespread than private ones, because we usually want inheriting classes to gain access to them.
5757

5858
Protected fields are not implemented in JavaScript on the language level, but in practice they are very convenient, so they are emulated.
5959

@@ -297,7 +297,7 @@ Supportable
297297

298298
**If we strictly delimit the internal interface, then the developer of the class can freely change its internal properties and methods, even without informing the users.**
299299

300-
If you're a developer of such class, it's great to know that private methods can be safely renamed, their parameters can be changed, and even removed, because no external code depends on them.
300+
If you're a developer of such class, it's great to know that private methods can be safely renamed, their parameters can be changed, and even removed, because no external code depends on them.
301301

302302
For users, when a new version comes out, it may be a total overhaul internally, but still simple to upgrade if the external interface is the same.
303303

Diff for: 1-js/09-classes/05-extend-natives/article.md

+8-2
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ alert(filteredArr); // 10, 50
2121
alert(filteredArr.isEmpty()); // false
2222
```
2323

24-
Please note a very interesting thing. Built-in methods like `filter`, `map` and others -- return new objects of exactly the inherited type. They rely on the `constructor` property to do so.
24+
Please note a very interesting thing. Built-in methods like `filter`, `map` and others -- return new objects of exactly the inherited type `PowerArray`. Their internal implementation uses object `constructor` property for that.
2525

2626
In the example above,
2727
```js
2828
arr.constructor === PowerArray
2929
```
3030

31-
So when `arr.filter()` is called, it internally creates the new array of results using exactly `new PowerArray`, not basic `Array`. That's actually very cool, because we can keep using `PowerArray` methods further on the result.
31+
When `arr.filter()` is called, it internally creates the new array of results using exactly `arr.constructor`, not basic `Array`. That's actually very cool, because we can keep using `PowerArray` methods further on the result.
3232

3333
Even more, we can customize that behavior.
3434

@@ -64,6 +64,10 @@ alert(filteredArr.isEmpty()); // Error: filteredArr.isEmpty is not a function
6464

6565
As you can see, now `.filter` returns `Array`. So the extended functionality is not passed any further.
6666

67+
```smart header="Other collections work similarly"
68+
Other collections, such as `Map` and `Set`, work alike. They also use `Symbol.species`.
69+
```
70+
6771
## No static inheritance in built-ins
6872

6973
Built-in objects have their own static methods, for instance `Object.keys`, `Array.isArray` etc.
@@ -81,3 +85,5 @@ Here's the picture structure for `Date` and `Object`:
8185
![](object-date-inheritance.svg)
8286

8387
As you can see, there's no link between `Date` and `Object`. They are independent, only `Date.prototype` inherits from `Object.prototype`.
88+
89+
That's an important difference of inheritance between built-in objects compared to what we get with `extends`.

Diff for: 1-js/09-classes/06-instanceof/article.md

+22-17
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ The syntax is:
1111
obj instanceof Class
1212
```
1313

14-
It returns `true` if `obj` belongs to the `Class` (or a class inheriting from it).
14+
It returns `true` if `obj` belongs to the `Class` or a class inheriting from it.
1515

1616
For instance:
1717

@@ -46,15 +46,17 @@ alert( arr instanceof Object ); // true
4646

4747
Please note that `arr` also belongs to the `Object` class. That's because `Array` prototypally inherits from `Object`.
4848

49-
The `instanceof` operator examines the prototype chain for the check, but we can set a custom logic in the static method `Symbol.hasInstance`.
49+
Normally, `instanceof` operator examines the prototype chain for the check. We can also set a custom logic in the static method `Symbol.hasInstance`.
5050

5151
The algorithm of `obj instanceof Class` works roughly as follows:
5252

53-
1. If there's a static method `Symbol.hasInstance`, then just call it: `Class[Symbol.hasInstance](obj)`. It should return either `true` or `false`. We're done.
54-
For example:
53+
1. If there's a static method `Symbol.hasInstance`, then just call it: `Class[Symbol.hasInstance](obj)`. It should return either `true` or `false`, and we're done. That's how we can customize the behavior of `instanceof`.
54+
55+
For example:
5556

5657
```js run
57-
// setup instanceOf check that assumes that anything that canEat is an animal
58+
// setup instanceOf check that assumes that
59+
// anything with canEat property is an animal
5860
class Animal {
5961
static [Symbol.hasInstance](obj) {
6062
if (obj.canEat) return true;
@@ -68,17 +70,19 @@ The algorithm of `obj instanceof Class` works roughly as follows:
6870

6971
2. Most classes do not have `Symbol.hasInstance`. In that case, the standard logic is used: `obj instanceOf Class` checks whether `Class.prototype` equals to one of prototypes in the `obj` prototype chain.
7072

71-
In other words, compare:
73+
In other words, compare one after another:
7274
```js
73-
obj.__proto__ === Class.prototype
74-
obj.__proto__.__proto__ === Class.prototype
75-
obj.__proto__.__proto__.__proto__ === Class.prototype
75+
obj.__proto__ === Class.prototype?
76+
obj.__proto__.__proto__ === Class.prototype?
77+
obj.__proto__.__proto__.__proto__ === Class.prototype?
7678
...
79+
// if any answer is true, return true
80+
// otherwise, if we reached the end of the chain, return false
7781
```
7882

79-
In the example above `Rabbit.prototype === rabbit.__proto__`, so that gives the answer immediately.
83+
In the example above `rabbit.__proto__ === Rabbit.prototype`, so that gives the answer immediately.
8084

81-
In the case of an inheritance, `rabbit` is an instance of the parent class as well:
85+
In the case of an inheritance, the match will be at the second step:
8286

8387
```js run
8488
class Animal {}
@@ -88,8 +92,11 @@ The algorithm of `obj instanceof Class` works roughly as follows:
8892
*!*
8993
alert(rabbit instanceof Animal); // true
9094
*/!*
95+
9196
// rabbit.__proto__ === Rabbit.prototype
97+
*!*
9298
// rabbit.__proto__.__proto__ === Animal.prototype (match!)
99+
*/!*
93100
```
94101

95102
Here's the illustration of what `rabbit instanceof Animal` compares with `Animal.prototype`:
@@ -100,7 +107,7 @@ By the way, there's also a method [objA.isPrototypeOf(objB)](mdn:js/object/isPro
100107

101108
That's funny, but the `Class` constructor itself does not participate in the check! Only the chain of prototypes and `Class.prototype` matters.
102109
103-
That can lead to interesting consequences when `prototype` is changed.
110+
That can lead to interesting consequences when `prototype` property is changed after the object is created.
104111
105112
Like here:
106113
@@ -117,8 +124,6 @@ alert( rabbit instanceof Rabbit ); // false
117124
*/!*
118125
```
119126
120-
That's one of the reasons to avoid changing `prototype`. Just to keep safe.
121-
122127
## Bonus: Object.prototype.toString for the type
123128
124129
We already know that plain objects are converted to string as `[object Object]`:
@@ -152,7 +157,7 @@ let objectToString = Object.prototype.toString;
152157
// what type is this?
153158
let arr = [];
154159
155-
alert( objectToString.call(arr) ); // [object Array]
160+
alert( objectToString.call(arr) ); // [object *!*Array*/!*]
156161
```
157162
158163
Here we used [call](mdn:js/function/call) as described in the chapter [](info:call-apply-decorators) to execute the function `objectToString` in the context `this=arr`.
@@ -196,11 +201,11 @@ As you can see, the result is exactly `Symbol.toStringTag` (if exists), wrapped
196201
197202
At the end we have "typeof on steroids" that not only works for primitive data types, but also for built-in objects and even can be customized.
198203
199-
It can be used instead of `instanceof` for built-in objects when we want to get the type as a string rather than just to check.
204+
We can use `{}.toString.call` instead of `instanceof` for built-in objects when we want to get the type as a string rather than just to check.
200205
201206
## Summary
202207
203-
Let's recap the type-checking methods that we know:
208+
Let's summarize the type-checking methods that we know:
204209

205210
| | works for | returns |
206211
|---------------|-------------|---------------|

Diff for: 1-js/09-classes/07-mixins/article.md

+10-8
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
In JavaScript we can only inherit from a single object. There can be only one `[[Prototype]]` for an object. And a class may extend only one other class.
44

5-
But sometimes that feels limiting. For instance, I have a class `StreetSweeper` and a class `Bicycle`, and want to make a `StreetSweepingBicycle`.
5+
But sometimes that feels limiting. For instance, I have a class `StreetSweeper` and a class `Bicycle`, and want to make their mix: a `StreetSweepingBicycle`.
66

7-
Or, talking about programming, we have a class `User` and a class `EventEmitter` that implements event generation, and we'd like to add the functionality of `EventEmitter` to `User`, so that our users can emit events.
7+
Or we have a class `User` and a class `EventEmitter` that implements event generation, and we'd like to add the functionality of `EventEmitter` to `User`, so that our users can emit events.
88

99
There's a concept that can help here, called "mixins".
1010

@@ -14,7 +14,7 @@ In other words, a *mixin* provides methods that implement a certain behavior, bu
1414

1515
## A mixin example
1616

17-
The simplest way to make a mixin in JavaScript is to make an object with useful methods, so that we can easily merge them into a prototype of any class.
17+
The simplest way to implement a mixin in JavaScript is to make an object with useful methods, so that we can easily merge them into a prototype of any class.
1818

1919
For instance here the mixin `sayHiMixin` is used to add some "speech" for `User`:
2020

@@ -75,10 +75,10 @@ let sayHiMixin = {
7575
*!*
7676
// call parent method
7777
*/!*
78-
super.say(`Hello ${this.name}`);
78+
super.say(`Hello ${this.name}`); // (*)
7979
},
8080
sayBye() {
81-
super.say(`Bye ${this.name}`);
81+
super.say(`Bye ${this.name}`); // (*)
8282
}
8383
};
8484

@@ -95,11 +95,13 @@ Object.assign(User.prototype, sayHiMixin);
9595
new User("Dude").sayHi(); // Hello Dude!
9696
```
9797

98-
Please note that the call to the parent method `super.say()` from `sayHiMixin` looks for the method in the prototype of that mixin, not the class.
98+
Please note that the call to the parent method `super.say()` from `sayHiMixin` (at lines labelled with `(*)`) looks for the method in the prototype of that mixin, not the class.
99+
100+
Here's the diagram (see the right part):
99101

100102
![](mixin-inheritance.svg)
101103

102-
That's because methods `sayHi` and `sayBye` were initially created in `sayHiMixin`. So their `[[HomeObject]]` internal property references `sayHiMixin`, as shown on the picture above.
104+
That's because methods `sayHi` and `sayBye` were initially created in `sayHiMixin`. So even though they got copied, their `[[HomeObject]]` internal property references `sayHiMixin`, as shown on the picture above.
103105

104106
As `super` looks for parent methods in `[[HomeObject]].[[Prototype]]`, that means it searches `sayHiMixin.[[Prototype]]`, not `User.[[Prototype]]`.
105107

@@ -199,7 +201,7 @@ And `eventMixin` mixin makes it easy to add such behavior to as many classes as
199201

200202
*Mixin* -- is a generic object-oriented programming term: a class that contains methods for other classes.
201203

202-
Some other languages like e.g. Python allow to create mixins using multiple inheritance. JavaScript does not support multiple inheritance, but mixins can be implemented by copying methods into prototype.
204+
Some other languages like allow multiple inheritance. JavaScript does not support multiple inheritance, but mixins can be implemented by copying methods into prototype.
203205

204206
We can use mixins as a way to augment a class by multiple behaviors, like event-handling as we have seen above.
205207

0 commit comments

Comments
 (0)