Skip to content

Commit f968724

Browse files
committed
minor
1 parent 800d47c commit f968724

File tree

2 files changed

+50
-35
lines changed

2 files changed

+50
-35
lines changed

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

+23-11
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class Animal {
1717
}
1818
stop() {
1919
this.speed = 0;
20-
alert(`${this.name} stopped.`);
20+
alert(`${this.name} stands still.`);
2121
}
2222
}
2323

@@ -65,7 +65,7 @@ class Animal {
6565
}
6666
stop() {
6767
this.speed = 0;
68-
alert(`${this.name} stopped.`);
68+
alert(`${this.name} stands still.`);
6969
}
7070
}
7171

@@ -155,7 +155,7 @@ class Animal {
155155
156156
stop() {
157157
this.speed = 0;
158-
alert(`${this.name} stopped.`);
158+
alert(`${this.name} stands still.`);
159159
}
160160
161161
}
@@ -176,7 +176,7 @@ class Rabbit extends Animal {
176176
let rabbit = new Rabbit("White Rabbit");
177177
178178
rabbit.run(5); // White Rabbit runs with speed 5.
179-
rabbit.stop(); // White Rabbit stopped. White rabbit hides!
179+
rabbit.stop(); // White Rabbit stands still. White rabbit hides!
180180
```
181181
182182
Now `Rabbit` has the `stop` method that calls the parent `super.stop()` in the process.
@@ -265,12 +265,12 @@ In JavaScript, there's a distinction between a "constructor function of an inher
265265

266266
The difference is:
267267

268-
- When a normal constructor runs, it creates an empty object as `this` and continues with it.
269-
- But when a derived constructor runs, it doesn't do it. It expects the parent constructor to do this job.
268+
- When a normal constructor runs, it creates an empty object and assigns it to `this`.
269+
- But when a derived constructor runs, it doesn't do this. It expects the parent constructor to do this job.
270270

271-
So if we're making a constructor of our own, then we must call `super`, because otherwise the object with `this` reference to it won't be created. And we'll get an error.
271+
So if we're making a constructor of our own, then we must call `super`, because otherwise the object for `this` won't be created. And we'll get an error.
272272

273-
For `Rabbit` to work, we need to call `super()` before using `this`, like here:
273+
For `Rabbit` constructor to work, it needs to call `super()` before using `this`, like here:
274274

275275
```js run
276276
class Animal {
@@ -306,16 +306,24 @@ alert(rabbit.earLength); // 10
306306

307307
## Super: internals, [[HomeObject]]
308308

309+
```warn header="Advanced information"
310+
If you're reading the tutorial for the first time - this section may be skipped.
311+
312+
It's about the internal mechanisms behind inheritance and `super`.
313+
```
314+
309315
Let's get a little deeper under the hood of `super`. We'll see some interesting things by the way.
310316

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

313-
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.
319+
Yeah, indeed, let's ask ourselves, how it should technically work? When an object method runs, it gets the current object as `this`. If we call `super.method()` then, the engine needs to get the `method` from the prototype of the current object. But how?
314320

315321
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.
316322

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

325+
You may skip this part and go below to the `[[HomeObject]]` subsection if you don't want to know the details. That won't harm. Or read on if you're interested in understanding things in-depth.
326+
319327
In the example below, `rabbit.__proto__ = animal`. Now let's try: in `rabbit.eat()` we'll call `animal.eat()`, using `this.__proto__`:
320328

321329
```js run
@@ -459,7 +467,7 @@ The very existance of `[[HomeObject]]` violates that principle, because methods
459467

460468
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.
461469

462-
Here's the demo of a wrong `super` call:
470+
Here's the demo of a wrong `super` result after copying:
463471
464472
```js run
465473
let animal = {
@@ -468,6 +476,7 @@ let animal = {
468476
}
469477
};
470478
479+
// rabbit inherits from animal
471480
let rabbit = {
472481
__proto__: animal,
473482
sayHi() {
@@ -481,6 +490,7 @@ let plant = {
481490
}
482491
};
483492
493+
// tree inherits from plant
484494
let tree = {
485495
__proto__: plant,
486496
*!*
@@ -497,9 +507,11 @@ A call to `tree.sayHi()` shows "I'm an animal". Definitevely wrong.
497507

498508
The reason is simple:
499509
- In the line `(*)`, the method `tree.sayHi` was copied from `rabbit`. Maybe we just wanted to avoid code duplication?
500-
- So its `[[HomeObject]]` is `rabbit`, as it was created in `rabbit`. There's no way to change `[[HomeObject]]`.
510+
- Its `[[HomeObject]]` is `rabbit`, as it was created in `rabbit`. There's no way to change `[[HomeObject]]`.
501511
- The code of `tree.sayHi()` has `super.sayHi()` inside. It goes up from `rabbit` and takes the method from `animal`.
502512
513+
Here's the diagram of what happens:
514+
503515
![](super-homeobject-wrong.svg)
504516

505517
### Methods, not function properties

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

+27-24
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11

22
# Static properties and methods
33

4-
We can also assign a method to the class function, not to its `"prototype"`. Such methods are called *static*.
4+
We can also assign a method to the class function itself, not to its `"prototype"`. Such methods are called *static*.
55

6-
An example:
6+
In a class, they are prepended by `static` keyword, like this:
77

88
```js run
99
class User {
@@ -17,7 +17,7 @@ class User {
1717
User.staticMethod(); // true
1818
```
1919

20-
That actually does the same as assigning it as a property:
20+
That actually does the same as assigning it as a property directly:
2121

2222
```js
2323
class User() { }
@@ -27,11 +27,11 @@ User.staticMethod = function() {
2727
};
2828
```
2929

30-
The value of `this` inside `User.staticMethod()` is the class constructor `User` itself (the "object before dot" rule).
30+
The value of `this` in `User.staticMethod()` call is the class constructor `User` itself (the "object before dot" rule).
3131

3232
Usually, static methods are used to implement functions that belong to the class, but not to any particular object of it.
3333

34-
For instance, we have `Article` objects and need a function to compare them. The natural choice would be `Article.compare`, like this:
34+
For instance, we have `Article` objects and need a function to compare them. A natural solution would be to add `Article.compare` method, like this:
3535

3636
```js run
3737
class Article {
@@ -61,13 +61,13 @@ articles.sort(Article.compare);
6161
alert( articles[0].title ); // CSS
6262
```
6363

64-
Here `Article.compare` stands "over" the articles, as a means to compare them. It's not a method of an article, but rather of the whole class.
64+
Here `Article.compare` stands "above" articles, as a means to compare them. It's not a method of an article, but rather of the whole class.
6565

6666
Another example would be a so-called "factory" method. Imagine, we need few ways to create an article:
6767

6868
1. Create by given parameters (`title`, `date` etc).
6969
2. Create an empty article with today's date.
70-
3. ...
70+
3. ...or else somehow.
7171

7272
The first way can be implemented by the constructor. And for the second one we can make a static method of the class.
7373

@@ -107,7 +107,7 @@ Article.remove({id: 12345});
107107

108108
[recent browser=Chrome]
109109

110-
Static properties are also possible, just like regular class properties:
110+
Static properties are also possible, they look like regular class properties, but prepended by `static`:
111111

112112
```js run
113113
class Article {
@@ -123,9 +123,9 @@ That is the same as a direct assignment to `Article`:
123123
Article.publisher = "Ilya Kantor";
124124
```
125125

126-
## Statics and inheritance
126+
## Inheritance of static methods
127127

128-
Statics are inherited, we can access `Parent.method` as `Child.method`.
128+
Static methods are inherited.
129129

130130
For instance, `Animal.compare` in the code below is inherited and accessible as `Rabbit.compare`:
131131

@@ -169,36 +169,39 @@ rabbits.sort(Rabbit.compare);
169169
rabbits[0].run(); // Black Rabbit runs with speed 5.
170170
```
171171

172-
Now we can call `Rabbit.compare` assuming that the inherited `Animal.compare` will be called.
172+
Now when we can call `Rabbit.compare`, the inherited `Animal.compare` will be called.
173173

174174
How does it work? Again, using prototypes. As you might have already guessed, `extends` gives `Rabbit` the `[[Prototype]]` reference to `Animal`.
175175

176-
177176
![](animal-rabbit-static.svg)
178177

179-
So, `Rabbit` function now inherits from `Animal` function. And `Animal` function normally has `[[Prototype]]` referencing `Function.prototype`, because it doesn't `extend` anything.
178+
So, `Rabbit extends Animal` creates two `[[Prototype]]` references:
179+
180+
1. `Rabbit` function prototypally inherits from `Animal` function.
181+
2. `Rabbit.prototype` prototypally inherits from `Animal.prototype`.
180182

181-
Here, let's check that:
183+
As the result, inheritance works both for regular and static methods.
184+
185+
Here, let's check that by code:
182186

183187
```js run
184188
class Animal {}
185189
class Rabbit extends Animal {}
186190

187-
// for static properties and methods
191+
// for statics
188192
alert(Rabbit.__proto__ === Animal); // true
189193

190-
// the next step up leads to Function.prototype
191-
alert(Animal.__proto__ === Function.prototype); // true
192-
193-
// the "normal" prototype chain for object methods
194+
// for regular methods
194195
alert(Rabbit.prototype.__proto__ === Animal.prototype);
195196
```
196197

197-
This way `Rabbit` has access to all static methods of `Animal`.
198-
199198
## Summary
200199

201-
Static methods are used for the functionality that doesn't relate to a concrete class instance, doesn't require an instance to exist, but rather belongs to the class as a whole, like `Article.compare` -- a generic method to compare two articles.
200+
Static methods are used for the functionality that belongs to the class "as a whole", doesn't relate to a concrete class instance.
201+
202+
For example, a method for comparison `Article.compare(article1, article2)` or a factory method `Article.createTodays()`.
203+
204+
They are labeled by the word `static` in class declaration.
202205

203206
Static properties are used when we'd like to store class-level data, also not bound to an instance.
204207

@@ -214,7 +217,7 @@ class MyClass {
214217
}
215218
```
216219

217-
That's technically the same as assigning to the class itself:
220+
Technically, static declaration is the same as assigning to the class itself:
218221

219222
```js
220223
MyClass.property = ...
@@ -223,4 +226,4 @@ MyClass.method = ...
223226

224227
Static properties are inherited.
225228

226-
Technically, 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`.
229+
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`.

0 commit comments

Comments
 (0)