Это может помочь разделить цепочки прототипов на две категории.
Рассмотрим конструктор:
function Person() {}
Значение Object.getPrototypeOf(Person)
является функцией. На самом деле это Function.prototype
. Поскольку Person
был создан как функция, он использует тот же объект-прототип, что и все функции. Это то же самое, что и Person.__proto__
, но это свойство не должно использоваться. В любом случае, с Object.getPrototypeOf(Person)
вы фактически поднимаетесь по лестнице того, что называется цепочкой прототипов.
Цепочка в направлении вверх выглядит так:
Person
→ Function.prototype
→ Object.prototype
(конечная точка)
Важно то, что эта цепочка прототипов имеет мало общего с объектами, которые Person
может построить . Эти построенные объекты имеют свою собственную цепочку прототипов, и эта цепочка потенциально может не иметь близких предков, общих с упомянутой выше.
Возьмем для примера этот объект:
var p = new Person();
p не имеет прямого отношения прототип-цепочка с Person . Их отношения разные. Объект p имеет собственную цепочку прототипов. Используя Object.getPrototypeOf
, вы обнаружите, что цепочка выглядит следующим образом:
p
→ Person.prototype
→ Object.prototype
(конечная точка)
В этой цепочке нет функциональных объектов (хотя это может быть).
Так что Person
похоже связано с двумя видами цепей, которые живут своей жизнью. Чтобы «перепрыгнуть» из одной цепочки в другую, вы используете:
.prototype
: перейти от цепочки конструктора к цепочке созданного объекта. Таким образом, это свойство определяется только для функциональных объектов (поскольку new
может использоваться только для функций).
.constructor
: перейти от цепочки созданного объекта к цепочке конструктора.
Вот визуальное представление двух задействованных цепочек прототипов, представленных в виде столбцов:
Подведем итог:
Свойство prototype
не дает информации о цепочке прототипов субъекта *1071*, но об объектах , созданных субъектом.
Неудивительно, что название объекта prototype
может привести к путанице. Возможно, было бы яснее, если бы это свойство было названо prototypeOfConstructedInstances
или что-то подобное.
Вы можете прыгать туда-сюда между двумя цепями прототипов:
Person.prototype.constructor === Person
Эта симметрия может быть нарушена путем явного присвоения другого объекта свойству prototype
(подробнее об этом позже).
Создать одну функцию, получить два объекта
Person.prototype
- это объект, который был создан одновременно с созданием функции Person
. Он имеет Person
в качестве конструктора, хотя этот конструктор еще не выполнялся. Итак, два объекта создаются одновременно:
- Функция
Person
сама
- Объект, который будет действовать как прототип, когда функция вызывается как конструктор
Оба являются объектами, но они выполняют разные роли: объект функции создает , в то время как другой объект представляет прототип любого объекта, который будет сконструирована функцией. Прототип объекта станет родителем построенного объекта в его цепочке прототипов.
Поскольку функция также является объектом, она также имеет своего родителя в собственной цепочке прототипов, но напомним, что эти две цепочки связаны с разными вещами.
Вот некоторые равенства, которые могут помочь разобраться в проблеме - все это напечатано true
:
function Person() {};
// This is prototype chain info for the constructor (the function object):
console.log(Object.getPrototypeOf(Person) === Function.prototype);
// Step further up in the same hierarchy:
console.log(Object.getPrototypeOf(Function.prototype) === Object.prototype);
console.log(Object.getPrototypeOf(Object.prototype) === null);
console.log(Person.__proto__ === Function.prototype);
// Here we swap lanes, and look at the constructor of the constructor
console.log(Person.constructor === Function);
console.log(Person instanceof Function);
// Person.prototype was created by Person (at the time of its creation)
// Here we swap lanes back and forth:
console.log(Person.prototype.constructor === Person);
// Although it is not an instance of it:
console.log(!(Person.prototype instanceof Person));
// Instances are objects created by the constructor:
var p = new Person();
// Similarly to what was shown for the constructor, here we have
// the same for the object created by the constructor:
console.log(Object.getPrototypeOf(p) === Person.prototype);
console.log(p.__proto__ === Person.prototype);
// Here we swap lanes, and look at the constructor
console.log(p.constructor === Person);
console.log(p instanceof Person);
Добавление уровней в цепочку прототипов
Хотя объект-прототип создается при создании функции конструктора, вы можете игнорировать этот объект и назначить другой объект, который следует использовать в качестве прототипа для любых последующих экземпляров, созданных этим конструктором.
Например:
function Thief() { }
var p = new Person();
Thief.prototype = p; // this determines the prototype for any new Thief objects:
var t = new Thief();
Нетw прототип цепочки t на один шаг длиннее, чем p :
t
→ p
→ Person.prototype
→ Object.prototype
(конечная точка)
Другая цепочка прототипов не длиннее: Thief
и Person
- это братья и сестры, имеющие одного и того же родителя в цепочке прототипов:
Person
}
Thief
} → Function.prototype
→ Object.prototype
(конечная точка)
Ранее представленный рисунок может быть расширен до этого (оригинал Thief.prototype
опущен):
Синие линии представляют цепочки прототипов, другие цветные линии представляют другие отношения:
- между объектом и его конструктором
- между конструктором и объектом-прототипом, который будет использоваться для конструирования объектов