Function и Object экземпляры друг друга
Maksym PohribniakОбъяснение, почему Function является экземпляром Object, и наоборот.
Прежде чем перейти непосредственно к объяснению, мы должны сначала кратко затронуть две темы: разницу между внутренним свойством [[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ылка на оригинал.