Класс расширяет объект?
Как мы уже знаем, все объекты наследуют от 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("Кроль");
alert( rabbit.hasOwnProperty('name') ); // Ошибка
Сперва давайте разберёмся, почему код не работает.
Причина становится очевидна, если мы попытаемся запустить его. Унаследованный конструктор класса должен вызывать super(). В противном случае "this" будет не определён.
Решение:
class Rabbit extends Object {
constructor(name) {
super(); // надо вызвать конструктор родителя, когда наследуемся
this.name = name;
}
}
let rabbit = new Rabbit("Кроль");
alert( rabbit.hasOwnProperty('name') ); // true
Но это ещё не все.
Даже после исправления есть важное различие между "class Rabbit extends Object" и class Rabbit.
Как мы знаем, синтаксис «extends» устанавливает 2 прототипа:
- Между
"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
Но если явно не наследуем от объекта, то для 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}) ); // Ошибка
Таким образом, в этом случае у 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 |