вернуться к уроку

Класс расширяет объект?

важность: 3

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

  1. Между "prototype" функций-конструкторов (для методов)
  2. Между самими функциями-конструкторами (для статических методов).

В случае с 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