Function и Object экземпляры друг друга

Function и Object экземпляры друг друга

Maksym Pohribniak

Объяснение, почему Function является экземпляром Object, и наоборот.

JavaScript во всей красе

Прежде чем перейти непосредственно к объяснению, мы должны сначала кратко затронуть две темы: разницу между внутренним свойством [[Prototype]] и внешним свойством prototype, и их взаимосвязью друг с другом. Если вы четко понимаете разницу между ними – сразу переходите к объяснению ниже.

Internal [[Prototype]], external prototype

Для краткости я не буду описывать детали оператора instanceof (вы всегда можете проверить спецификацию ECMAScript), он просто проверяет, имеет ли объект в своей цепочке прототипов свойство конструктора prototype. Но тестирование цепочки прототипов не означает поиск его внешнего свойства , с которым все знакомы, - это относится к внутреннему свойству [[Prototype]]:

Все объекты имеют внутреннее свойство, называемое [[Prototype]]. Значение этого свойства равно null или Object и используется для реализации наследования.

Будучи внутренним свойством, [[Prototype]] не доступен напрямую, однако с ним можно взаимодействовать с помощью Object.getPrototypeOf. Этот метод  возвращает прототип (то есть, внутреннее свойство [[Prototype]]) указанного объекта.

В свою очередь, внешнее свойство prototype является специфичным для объектов Function:

Значение свойства prototype используется для инициализации внутреннего свойства [[Prototype]] вновь созданного объекта, прежде чем объект Function вызывается в качестве конструктора для этого вновь созданного объекта.

Это означает, что prototype - это объект, который используется для построения [[Prototype]] при вызове функции с использованием ключевого слова new. Другими словами, когда функция используется в качестве конструктора, будет создан новый объект, и у нового объекта в [[Prototype]] будет инициализировано его свойство prototype.

Рассмотрим следующий пример:

// Функция, которая будет использоваться в качестве конструктора function Car()(){}

// у Car.prototype есть только одно свойство - "constructor" 
// который указывает на функцию Car
Car.prototype.constructor === Car // true

// Car.prototype - это объект
// console.log(Car.prototype) => Object {}
Car.prototype instanceof Object // true

// Создание оэкземпляра  с помощью  ключевого слова new
var myCar = new Car()

// Инициализируется внутренний [[Prototype]] `myCar` 
// с `Car.prototype`
Object.getPrototypeOf(myCar) === Car.prototype // true

С помощью приведенного выше фрагмента мы можем сформулировать следующие пункты:

  • Car.prototype указывает на автоматически созданный новый объект. Это то, что JavaScript делает по умолчанию, когда вы объявляете функцию.
  • Car.prototype пуст, за исключением свойства конструктора, которое указывает на саму функцию конструктора.
  • Car.prototype является экземпляром объекта.
  • [[Prototype]] myCar - это Car.prototype.
  • Следующим объектом в цепи прототипов myCar (т.е. [[Prototype]]) является Car.prototype.

Последняя строка (Object.getPrototypeOf (myCar) = Car.prototype) демонстрирует, что myCar действительно является экземпляром Car. Мы можем проверить это оператором instanceof:

myCar instanceof Car // true

Оператор instanceOf идет вверх по цепочке прототипов, чтобы определить, равна ли левая сторона [[Prototype]] прототипу (prototype) правой части.

Теперь, когда мы понимаем связь между [[Prototype]] и prototype, давайте вернемся и рассмотрим, почему Function и Object являются экземплярами друг друга.

Function instanceof Object

var internalProto{}
internalProto = Object.getPrototypeOf(Function)
internalProto === Object.prototype // false
// Так как это неверно, двигайтесь вверх по цепочке прототипов
internalProto = Object.getPrototypeOf(internalProto)
internalProto === Object.prototype // true
// Так как это правда, нет необходимости двигаться вверх по цепочке

Помните, что все объекты в JavaScript наследуются от Object. Это касается и Function:

Когда Function вызывается как функция, а не как конструктор, она создает и инициализирует новый  Function object. Таким образом, вызов функции Function (…) эквивалентен выражению создания объекта new Function (…) с теми же аргументами.

Кроме того, Function.prototype инициализируется той же функцией, что и его свойство [[Prototype]]:

Каждая встроенная функция и каждый встроенный конструктор имеет объект Function prototype, который является начальным значением выражения Function.prototype в качестве значения его внутреннего свойства [[Prototype]].

Все это значит следующее:

Function.prototype // function () {}
Object.getPrototypeOf(Function) // function () {}

// Они указывают на одну и ту же функцию
Object.getPrototypeOf(Function) === Function.prototype // true

Следовательно, поскольку Function.prototype является функцией, а каждая функция является Object, то ее свойство [[Prototype]] должно в конечном итоге ссылаться на Object.prototype. Так как он ссылается на Object.prototype, то  Function instanceof Object  имеет значение true.

Object instanceof Function

var internalProto
internalProto = Object.getPrototypeOf(Object)
internalProto === Function.prototype // true
// Since it’s true, no need to move up the chain

Когда мы говорим Object.getPrototypeOf(Object), мы имеем в виду Object constructor. Конструктор действительно является функцией. Его поведение изложено в спецификации. Тем не менее, самое важное, что касается его отношения к Function, это утверждение:

Значением внутреннего свойства [[Prototype]] конструктора Object является стандартный объект-прототип встроенной Function .

Следовательно, поскольку Object [[Prototype]] является Function.prototype, а так как Function.prototype является функцией, то Object instanceof Function вернет true.


Я надеюсь, что у вас появилось понимание выражения в начале статьи. Если что-то неясно, пожалуйста, напишите в ЛС. Всегда буду рад помочь!

Сcылка на оригинал.

Report Page