Skip to content

Commit 6cf9e24

Browse files
committed
Merge branch 'master' of github.com:javascript-tutorial/en.javascript.info into sync-445bda39
2 parents d9caafe + 445bda3 commit 6cf9e24

File tree

5 files changed

+109
-31
lines changed

5 files changed

+109
-31
lines changed

1-js/02-first-steps/09-comparison/article.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ For example:
2626
alert( 2 > 1 ); // true (correct)
2727
alert( 2 == 1 ); // false (wrong)
2828
alert( 2 != 1 ); // true (correct)
29-
```
29+
```
3030

3131
A comparison result can be assigned to a variable, just like any value:
3232

1-js/05-data-types/02-number/article.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ There are two ways to do so:
148148

149149
1. Multiply-and-divide.
150150

151-
For example, to round the number to the 2nd digit after the decimal, we can multiply the number by `100`, call the rounding function and then divide it back.
151+
For example, to round the number to the 2nd digit after the decimal, we can multiply the number by `100` (or a bigger power of 10), call the rounding function and then divide it back.
152152
```js run
153153
let num = 1.23456;
154154

1-js/09-classes/01-class/article.md

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ alert(user.name); // John
331331
alert(User.prototype.name); // undefined
332332
```
333333

334-
Technically, they are processed after the constructor has done it's job, and we can use for them complex expressions and function calls:
334+
We can also assign values using more complex expressions and function calls:
335335

336336
```js run
337337
class User {
@@ -344,6 +344,7 @@ let user = new User();
344344
alert(user.name); // John
345345
```
346346

347+
347348
### Making bound methods with class fields
348349

349350
As demonstrated in the chapter <info:bind> functions in JavaScript have a dynamic `this`. It depends on the context of the call.
@@ -375,30 +376,9 @@ The problem is called "losing `this`".
375376
There are two approaches to fixing it, as discussed in the chapter <info:bind>:
376377

377378
1. Pass a wrapper-function, such as `setTimeout(() => button.click(), 1000)`.
378-
2. Bind the method to object, e.g. in the constructor:
379-
380-
```js run
381-
class Button {
382-
constructor(value) {
383-
this.value = value;
384-
*!*
385-
this.click = this.click.bind(this);
386-
*/!*
387-
}
388-
389-
click() {
390-
alert(this.value);
391-
}
392-
}
393-
394-
let button = new Button("hello");
395-
396-
*!*
397-
setTimeout(button.click, 1000); // hello
398-
*/!*
399-
```
379+
2. Bind the method to object, e.g. in the constructor.
400380

401-
Class fields provide a more elegant syntax for the latter solution:
381+
Class fields provide another, quite elegant syntax:
402382

403383
```js run
404384
class Button {
@@ -417,9 +397,9 @@ let button = new Button("hello");
417397
setTimeout(button.click, 1000); // hello
418398
```
419399

420-
The class field `click = () => {...}` creates an independent function on each `Button` object, with `this` bound to the object. Then we can pass `button.click` around anywhere, and it will be called with the right `this`.
400+
The class field `click = () => {...}` is created on a per-object basis, there's a separate function for each `Button` object, with `this` inside it referencing that object. We can pass `button.click` around anywhere, and the value of `this` will always be correct.
421401

422-
That's especially useful in browser environment, when we need to setup a method as an event listener.
402+
That's especially useful in browser environment, for event listeners.
423403

424404
## Summary
425405

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

Lines changed: 100 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,9 @@ let rabbit = new Rabbit("White Rabbit", 10); // Error: this is not defined.
230230

231231
Whoops! We've got an error. Now we can't create rabbits. What went wrong?
232232

233-
The short answer is: constructors in inheriting classes must call `super(...)`, and (!) do it before using `this`.
233+
The short answer is:
234+
235+
- **Constructors in inheriting classes must call `super(...)`, and (!) do it before using `this`.**
234236

235237
...But why? What's going on here? Indeed, the requirement seems strange.
236238

@@ -243,7 +245,7 @@ That label affects its behavior with `new`.
243245
- When a regular function is executed with `new`, it creates an empty object and assigns it to `this`.
244246
- But when a derived constructor runs, it doesn't do this. It expects the parent constructor to do this job.
245247

246-
So a derived constructor must call `super` in order to execute its parent (non-derived) constructor, otherwise the object for `this` won't be created. And we'll get an error.
248+
So a derived constructor must call `super` in order to execute its parent (base) constructor, otherwise the object for `this` won't be created. And we'll get an error.
247249

248250
For the `Rabbit` constructor to work, it needs to call `super()` before using `this`, like here:
249251

@@ -279,6 +281,102 @@ alert(rabbit.earLength); // 10
279281
```
280282

281283

284+
285+
### Overriding class fields: a tricky note
286+
287+
```warn header="Advanced note"
288+
This note assumes you have a certain experience with classes, maybe in other programming languages.
289+
290+
It provides better insight into the language and also explains the behavior that might be a source of bugs (but not very often).
291+
292+
If you find it difficult to understand, just go on, continue reading, then return to it some time later.
293+
```
294+
295+
We can override not only methods, but also class fields.
296+
297+
Although, there's a tricky behavior when we access an overridden field in parent constructor, quite different from most other programming languages.
298+
299+
Consider this example:
300+
301+
```js run
302+
class Animal {
303+
name = 'animal'
304+
305+
constructor() {
306+
alert(this.name); // (*)
307+
}
308+
}
309+
310+
class Rabbit extends Animal {
311+
name = 'rabbit';
312+
}
313+
314+
new Animal(); // animal
315+
*!*
316+
new Rabbit(); // animal
317+
*/!*
318+
```
319+
320+
Here, class `Rabbit` extends `Animal` and overrides `name` field with its own value.
321+
322+
There's no own constructor in `Rabbit`, so `Animal` constructor is called.
323+
324+
What's interesting is that in both cases: `new Animal()` and `new Rabbit()`, the `alert` in the line `(*)` shows `animal`.
325+
326+
**In other words, parent constructor always uses its own field value, not the overridden one.**
327+
328+
What's odd about it?
329+
330+
If it's not clear yet, please compare with methods.
331+
332+
Here's the same code, but instead of `this.name` field we call `this.showName()` method:
333+
334+
```js run
335+
class Animal {
336+
showName() { // instead of this.name = 'animal'
337+
alert('animal');
338+
}
339+
340+
constructor() {
341+
this.showName(); // instead of alert(this.name);
342+
}
343+
}
344+
345+
class Rabbit extends Animal {
346+
showName() {
347+
alert('rabbit');
348+
}
349+
}
350+
351+
new Animal(); // animal
352+
*!*
353+
new Rabbit(); // rabbit
354+
*/!*
355+
```
356+
357+
Please note: now the output is different.
358+
359+
And that's what we naturally expect. When the parent constructor is called in the derived class, it uses the overridden method.
360+
361+
...But for class fields it's not so. As said, the parent constructor always uses the parent field.
362+
363+
Why is there the difference?
364+
365+
Well, the reason is in the field initialization order. The class field is initialized:
366+
- Before constructor for the base class (that doesn't extend anything),
367+
- Imediately after `super()` for the derived class.
368+
369+
In our case, `Rabbit` is the derived class. There's no `constructor()` in it. As said previously, that's the same as if there was an empty constructor with only `super(...args)`.
370+
371+
So, `new Rabbit()` calls `super()`, thus executing the parent constructor, and (per the rule for derived classes) only after that its class fields are initialized. At the time of the parent constructor execution, there are no `Rabbit` class fields yet, that's why `Animal` fields are used.
372+
373+
This subtle difference between fields and methods is specific to JavaScript
374+
375+
Luckily, this behavior only reveals itself if an overridden field is used in the parent constructor. Then it may be difficult to understand what's going on, so we're explaining it here.
376+
377+
If it becomes a problem, one can fix it by using methods or getters/setters instead of fields.
378+
379+
282380
## Super: internals, [[HomeObject]]
283381

284382
```warn header="Advanced information"

9-regular-expressions/06-regexp-boundary/article.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ So, it matches the pattern `pattern:\bHello\b`, because:
2525

2626
1. At the beginning of the string matches the first test `pattern:\b`.
2727
2. Then matches the word `pattern:Hello`.
28-
3. Then the test `pattern:\b` matches again, as we're between `subject:o` and a space.
28+
3. Then the test `pattern:\b` matches again, as we're between `subject:o` and a comma.
2929

3030
The pattern `pattern:\bHello\b` would also match. But not `pattern:\bHell\b` (because there's no word boundary after `l`) and not `Java!\b` (because the exclamation sign is not a wordly character `pattern:\w`, so there's no word boundary after it).
3131

0 commit comments

Comments
 (0)