Skip to content

Commit f659d08

Browse files
authored
👾 smth
1 parent 35b99f0 commit f659d08

1 file changed

Lines changed: 94 additions & 0 deletions

File tree

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

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,100 @@ alert(rabbit.earLength); // 10
280280
*/!*
281281
```
282282

283+
### Переопределение полей класса: каверзное замечание
284+
285+
```warn header="Продвинутое замечание"
286+
В этом примечании предполагается, что у вас есть определенный опыт работы с классами, возможно, при работе с другими языками программирования.
287+
288+
Это дает лучшее представление о языке, а также объясняет поведение, которое может быть источником ошибок (но не очень часто).
289+
290+
Если вы нашли этот материал слишком сложным, просто продолжайте, продолжайте читать, а затем вернитесь к нему через некоторое время.
291+
```
292+
293+
Мы можем переопределять не только методы, но и поля класса.
294+
295+
Хотя, когда мы обращаемся к переопределенному полю в родительском конструкторе, возникает каверзное поведение, сильно отличающееся от большинства других языков программирования.
296+
297+
Рассмотрим этот пример:
298+
299+
```js run
300+
class Animal {
301+
name = 'animal';
302+
303+
constructor() {
304+
alert(this.name); // (*)
305+
}
306+
}
307+
308+
class Rabbit extends Animal {
309+
name = 'rabbit';
310+
}
311+
312+
new Animal(); // animal
313+
*!*
314+
new Rabbit(); // animal
315+
*/!*
316+
```
317+
318+
Здесь класс `Rabbit` расширяет `Animal` и переопределяет поле `name` своим собственным значением.
319+
320+
В `Rabbit` нет собственного конструктора, поэтому вызывается конструктор `Animal`.
321+
322+
Что интересно, так это то, что в обоих случаях: `new Animal()` и `new Rabbit()`, `alert` в строке `(*)` показывает `animal`.
323+
324+
**Другими словами, родительский конструктор всегда использует свое собственное значение поля, а не переопределенное.**
325+
326+
Что в этом странного?
327+
328+
Если это еще не ясно, пожалуйста, сравните с методами.
329+
330+
Вот тот же код, но вместо поля `this.name`, мы вызываем метод `this.showName()`:
331+
332+
```js run
333+
class Animal {
334+
showName() { // вместо this.name = 'animal'
335+
alert('animal');
336+
}
337+
338+
constructor() {
339+
this.showName(); // вместо alert(this.name);
340+
}
341+
}
342+
343+
class Rabbit extends Animal {
344+
showName() {
345+
alert('rabbit');
346+
}
347+
}
348+
349+
new Animal(); // animal
350+
*!*
351+
new Rabbit(); // rabbit
352+
*/!*
353+
```
354+
355+
Обратите внимание: теперь результат другой.
356+
357+
И это то, чего мы, естественно, ожидаем. Когда родительский конструктор вызывается в производном классе, он использует переопределенный метод.
358+
359+
...Но для полей класса это не так. Как уже было сказано, родительский конструктор всегда использует родительское поле.
360+
361+
Почему есть разница?
362+
363+
Ну, причина в порядке инициализации поля. Поле класса инициализируется:
364+
- Перед конструктором для базового класса (который ничего не расширяет),
365+
- Сразу после `super()` для производного класса.
366+
367+
В нашем случае `Rabbit` - это производный класс. В нем нет конструктора `constructor()`. Как было сказано ранее, это то же самое, как если бы существовал пустой конструктор только с `super(...args)`.
368+
369+
Итак, `new Rabbit()` вызывает `super()`, таким образом, выполняя родительский конструктор, и (согласно правилу для производных классов) только после этого инициализируются поля его класса. На момент выполнения родительского конструктора еще нет полей класса `Rabbit`, поэтому используются поля `Animal`.
370+
371+
Это тонкое различие между полями и методами специфично для JavaScript
372+
373+
К счастью, такое поведение проявляется только в том случае, если в родительском конструкторе используется переопределенное поле. Тогда может быть трудно понять, что происходит, поэтому мы объясняем это здесь.
374+
375+
Если это становится проблемой, ее можно исправить, используя методы или геттеры/сеттеры вместо полей.
376+
283377
## Устройство super, [[HomeObject]]
284378

285379
```warn header="Продвинутая информация"

0 commit comments

Comments
 (0)