Класс расширяет объект?
Как мы уже знаем, все объекты наследуют от 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 |