Клас розширює об’єкт?
Як ми знаємо, всі об’єкти зазвичай успадковуються від Object.prototype й отримують доступ до «загальних» методів об’єкта, наприклад hasOwnProperty тощо.
Наприклад:
class Rabbit {
constructor(name) {
this.name = name;
}
}
let rabbit = new Rabbit("Rab");
// метод hasOwnProperty від Object.prototype
alert( rabbit.hasOwnProperty('name') ); // true
Але що як ми пропишемо явно "class Rabbit extends Object" – тоді результат буде відрізнятися від звичайного "class Rabbit"?
Яка різниця?
Ось приклад такого коду (він не працює – чому? виправте його):
class Rabbit extends Object {
constructor(name) {
this.name = name;
}
}
let rabbit = new Rabbit("Rab");
alert( rabbit.hasOwnProperty('name') ); // Error
Спочатку подивімося, чому останній код не працює.
Причина стає очевидною, якщо ми спробуємо його запустити. Конструктор класу, що успадковується, повинен викликати super(). Інакше "this" буде не визначене.
Виправлення:
class Rabbit extends Object {
constructor(name) {
super(); // потрібно викликати батьківський конструктор під час успадкування
this.name = name;
}
}
let rabbit = new Rabbit("Rab");
alert( rabbit.hasOwnProperty('name') ); // true
Але це ще не все.
Навіть після виправлення все ще існує важлива різниця між "class Rabbit extends Object" та class Rabbit.
Як ми знаємо, синтаксис “extends” встановлює два прототипи:
- Між
"prototype"функцій-конструкторів (для методів). - Між самими функціями-конструкторами (для статичних методів).
У нашому випадку для class Rabbit extends Object це означає:
class Rabbit extends Object {}
alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true
alert( Rabbit.__proto__ === Object ); // (2) true
Отже, Rabbit тепер надає доступ до статичних методів Object через Rabbit, наприклад:
class Rabbit extends Object {}
// зазвичай ми викликаємо Object.getOwnPropertyNames
alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // a,b
Але якщо у нас немає extends Object, тоді для Rabbit.__proto__ не встановлено значення Object.
Приклад:
class Rabbit {}
alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true
alert( Rabbit.__proto__ === Object ); // (2) false (!)
alert( Rabbit.__proto__ === Function.prototype ); // як у будь-якої функції за замовчуванням
// помилка, такої функції в Rabbit немає
alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // Error
Тому Rabbit не надає доступу до статичних методів Object у цьому випадку.
До речі, Function.prototype має “загальні” методи функції, такі як call, bind тощо. Вони в кінцевому підсумку доступні в обох випадках, тому що для вбудованого конструктора Object, Object.__proto__ === Function.prototype.
Приклад на картинці:
Коротко кажучи, є дві відмінності:
| class Rabbit | class Rabbit extends Object |
|---|---|
| – | необхідно викликати super() в конструкторі |
Rabbit.__proto__ === Function.prototype |
Rabbit.__proto__ === Object |