You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
В этом примечании предполагается, что у вас есть определенный опыт работы с классами, возможно, при работе с другими языками программирования.
287
+
288
+
Это дает лучшее представление о языке, а также объясняет поведение, которое может быть источником ошибок (но не очень часто).
289
+
290
+
Если вы нашли этот материал слишком сложным, просто продолжайте, продолжайте читать, а затем вернитесь к нему через некоторое время.
291
+
```
292
+
293
+
Мы можем переопределять не только методы, но и поля класса.
294
+
295
+
Хотя, когда мы обращаемся к переопределенному полю в родительском конструкторе, возникает каверзное поведение, сильно отличающееся от большинства других языков программирования.
296
+
297
+
Рассмотрим этот пример:
298
+
299
+
```js run
300
+
classAnimal {
301
+
name ='animal';
302
+
303
+
constructor() {
304
+
alert(this.name); // (*)
305
+
}
306
+
}
307
+
308
+
classRabbitextendsAnimal {
309
+
name ='rabbit';
310
+
}
311
+
312
+
newAnimal(); // animal
313
+
*!*
314
+
newRabbit(); // 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
+
classAnimal {
334
+
showName() { // вместо this.name = 'animal'
335
+
alert('animal');
336
+
}
337
+
338
+
constructor() {
339
+
this.showName(); // вместо alert(this.name);
340
+
}
341
+
}
342
+
343
+
classRabbitextendsAnimal {
344
+
showName() {
345
+
alert('rabbit');
346
+
}
347
+
}
348
+
349
+
newAnimal(); // animal
350
+
*!*
351
+
newRabbit(); // 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
+
Если это становится проблемой, ее можно исправить, используя методы или геттеры/сеттеры вместо полей.
0 commit comments