Подтвердить что ты не робот

Объект JavaScript находит свой прототип с помощью конструктора?

В книге Секреты JavaScript Ninja, 2013, страница 125, говорится:

Каждый объект в JavaScript имеет неявное свойство с именем constructorкоторый ссылается на конструктор, который использовался для создания объекта. И поскольку прототип является свойством конструктор, каждый объект имеет способ найти свой прототип.

На самом деле это может быть одна из самых ошибочных вещей, которые я слышал о JavaScript, и она исходила от предположительно эксперта JavaScript. Не правда ли, что

  • любой объект JavaScript "имеет способ найти свой прототип" с использованием внутреннего свойства [[prototype]] (как в спецификации ECMA-262, стр. 32). Он доступен в Chrome и Firefox с помощью __proto__, а в более поздних версиях IE, используя Object.getPrototypeOf

  • любой объект получает свойство constructor, получая его в объекте прототипа, на который указывает __proto__. Свойство constructor иногда даже не устанавливается корректно, так как некоторые библиотеки JavaScript или фреймворки вообще не используют его. constructor - свойство объекта-прототипа, а не свойство самого объекта:

(как показано в инструменте разработчика Chrome):

> function Foo() {}
undefined

> var foo = new Foo()
undefined

> foo.hasOwnProperty("constructor")
false

> foo.__proto__.hasOwnProperty("constructor")
true

> foo.__proto__.constructor === Foo
true

Является ли приведенное выше (1) и (2) истинным? Что такое "неявное свойство с именем constructor" в JavaScript, как в цитируемом тексте? Старается ли это что-то вроде [[prototype]], являющегося внутренним свойством? Но что более важно, я хотел бы знать, верны ли (1) и (2) выше, а не то, что говорит цитируемый текст.

4b9b3361

Ответ 1

цитируемый текст очень точен и очень просто объясняет этот механизм.

"Каждый объект в JavaScript имеет неявное свойство named constructor, которое ссылается на конструктор, который использовался для создания объекта."

Это абсолютно верно, поскольку @Mathletics указала:

foo.constructor === Foo // true

... "И поскольку прототип является свойством конструктора, каждый объект имеет способ найти его прототип."

Это также можно понять просто, как прочитано. Получение прототипа из конструктора является допустимым способом для экземпляра, чтобы найти его прототип.

foo.constructor.prototype // Foo {}

А также

foo.constructor.prototype === foo.__proto__ // true

Я думаю, что способ, которым книга описывает это, является самой подходящей для этого. Свойство "__proto__" названо с двойным подчеркиванием с каждой стороны по причине. Как вы указали, это внутреннее свойство, а двойное подчеркивание является широко используемым соглашением для именования внутренних свойств. Он не "видим" с hasOwnProperty. Не потому, что это внутреннее свойство, а потому, что он не установлен напрямую на самом объекте. Это может лучше объяснить, что hasOwnPropery означает более четко:

foo.a = 4;
foo.a; // 4
foo.hasOwnProperty("a"); // true
foo.constructor.prototype.b = 5;
foo.b; // 5
foo.hasOwnProperty("b"); // false

Ответ 2

John Resig Wrong

Да, автор jQuery может ошибаться. Об этом он заявил:

Каждый объект в JavaScript имеет неявное свойство с именем constructor, которое ссылается на конструктор, который использовался для создания объекта. И поскольку прототип является свойством конструктора, каждый объект имеет способ найти его прототип.

Вот причины, по которым его утверждение ложно:

  • Не у каждого объекта есть прототип. Следовательно, эти объекты также не имеют никаких неявных свойств.
  • Не каждый объект, имеющий прототип, создается функцией-конструктором. Это, однако, не включает в себя литералы и функции объектов, массивов и регулярных выражений, поскольку эти объекты неявно создаются функциями-конструкторами.
  • Не каждый объект, имеющий прототип и созданный функцией конструктора, имеет неявное свойство с именем constructor.
  • Не каждый объект, имеющий прототип, создается конструктором и имеет неявное свойство с именем constructor имеет это свойство, указывающее на конструктор, создавший этот объект.
  • Не каждый объект, имеющий прототип, создается конструктором и имеет неявное свойство с именем constructor, которое указывает на функцию конструктора, которая создала этот объект, имеет свойство prototype в своей конструкторской функции.
  • Не каждый объект, имеющий прототип, создается конструктором, имеет неявное свойство с именем constructor, которое указывает на функцию-конструктор, которая создала этот объект и имеет свойство, называемое prototype в его конструкторской функции, имеет свойство указывает на прототип этого объекта.

Докажем эти утверждения на примере, чтобы доказать, что утверждение Джона Ресига ложно. Поскольку все эти утверждения начинаются с утверждения "не каждый объект", нам нужно всего лишь найти один пример каждого утверждения, чтобы доказать, что утверждение Джона Ресига ложно.

Доказательство для утверждения 1

Метод Object.create может использоваться для создания нового объекта и установки его внутреннего свойства [[prototype]]. Следовательно, его можно использовать для создания объекта, который не имеет прототипа:

var o = Object.create(null); // o has no prototype

Объект в приведенном выше примере не имеет прототипа - для этого внутреннего свойства [[prototype]] установлено значение null. Следовательно, он также не имеет никаких неявных свойств.

Доказательство утверждения 2

Теперь создадим еще один объект p, который наследует объект o следующим образом:

var p = Object.create(o); // the prototype of p is o

Следовательно, объект p имеет прототип, но он не был создан конструктором.

Доказательство утверждения 3

Хорошо давайте создадим объект p вместо конструктора (на самом деле это именно так, как реализована функция Object.create):

function F() {}  // F is a constructor
F.prototype = o; // objects constructed by F inherit from o
var p = new F;   // p is an object which is constructed by F

Здесь объект p создается конструктором F. Однако он не имеет никакого неявного свойства с именем constructor.

Доказательство для утверждения 4

Что делать, если переменной o присвоен объектный литерал, а затем используется как свойство prototype конструктора F?

var o = {};      // object literals inherit from Object.prototype
function F() {}  // F is a constructor
F.prototype = o; // objects constructed by F inherit from o
var p = new F;   // p is an object which is constructed by F

Теперь объект p имеет неявное свойство с именем constructor, но он указывает Object вместо F. Следовательно, p.constructor.prototype указывает на Object.prototype вместо o.

Доказательство утверждения 5

Возможно, вы думаете, что проблема заключается в наследовании? Хорошо, пусть вообще покончит с наследованием. Начиная с нуля:

var p = new F;      // p is constructed by F, it inherits from F.prototype
delete F.prototype; // devious isn't it? I love being naughty
function F() {}     // declarations are hoisted

Итак, теперь объект p наследуется от F.prototype и имеет неявное свойство с именем constructor, которое указывает на F. Однако, поскольку мы удалили свойство prototype из F, мы не можем получить доступ к прототипу p через p.constructor.prototype (он теперь вернет undefined).

Доказательство утверждения 6

Немного измените последний пример. Вместо удаления F.prototype мы перейдем к чему-то еще. Например:

var o = {};      // object literals inherit from Object.prototype
var p = new F;   // p is constructed by F, it inherits from F.prototype
F.prototype = o; // oops, what will happen now?
function F() {}  // declarations are hoisted

Теперь объект p наследует от F.prototype и имеет неявное свойство с именем constructor, которое указывает на F. Однако, поскольку мы устанавливаем F.prototype в o, когда мы обращаемся к p.constructor.prototype, мы получим o вместо исходного F.prototype.

Заключение

Как вы можете видеть, выражение Джона Ресига абсолютно неверно. Нам не нужно было 6 примеров, чтобы доказать это. Любой пример будет достаточным. Однако я хотел показать, насколько неверно его утверждение. Поэтому я написал все возможные примеры того, что я мог думать об этом, опровергая его утверждение.

JavaScript - это прототипный объектно-ориентированный язык программирования, который означает, что объекты наследуются от других объектов. Конструкторы не обязаны строго создавать объекты. Однако они имеют неоправданное значение, поскольку, к сожалению, способ прототипного наследования работает в JavaScript.

Конструктор-образец прототипального наследования часто запутывает и вводит в заблуждение. Кроме того, он скрывает истинный путь прототипного наследования, который не использует конструкторы (прототипная схема прототипного наследования). Чтобы узнать больше об этом, прочитайте следующий ответ: fooobar.com/questions/17945/...

Ответ 3

Как я это узнал и что можно наблюдать

Каждый объект имеет свойство __proto__, каждая функция имеет дополнительное свойство prototype. Это prototype является самим объектом и имеет свойство под названием constructor, которое указывает на исходную функцию:

function C(){}
console.log(C.hasOwnProperty("prototype")); //true
console.log(C.prototype.hasOwnProperty("constructor")); //true
console.log(C.prototype.constructor === C); //true

Когда объект создается, его свойство __proto__ устанавливается в свойство prototype его функции-конструктора:

var c = new C();
console.log(c.__proto__ === C.prototype) //true

Вся информация о том, что c, знает ли это из своего свойства __proto__. Это можно наблюдать, просто изменив свойство __proto__ вручную:

function D(){}
c.__proto__ = D.prototype;
console.log(c.constructor === D) //true
console.log(c instanceof D) //true

Итак, учитывая все это, было бы очень трудно поверить, что есть свойство конструктора, из которого все происходит. Но все еще может быть свойство внутреннего конструктора (т.е. __constr__), к которому мы не можем получить доступ, но к которому обращаются, когда запрашивается прототип. По крайней мере, можно было бы предположить.

Одна вещь, которая также меня немного смущает, такова:

console.log(c.hasOwnProperty("__proto__")) //false

Я предполагаю, что __proto__ просто не будет схвачен методом hasOwnProperty, но, возможно, кто-то еще может пролить свет на это.

FIDDLE

Что говорит ECMA

Вот также некоторые выдержки из Спецификация ECMA:

Все объекты имеют внутреннее свойство, называемое [[Prototype]]. Значение этого свойства является либо нулевым, либо объектом и используется для реализация наследования. Может ли собственный объект иметь хост-объект как его [[Прототип]] зависит от реализации. каждый Цепочка [[Prototype]] должна иметь конечную длину (то есть, начиная с любой объект, рекурсивный доступ к внутреннему свойству [[Prototype]] должно в конечном итоге привести к нулевому значению). Именованные свойства данных Объект [[Prototype]] наследуется (видимы как свойства дочерний объект) для доступа, но не для доступа. Именованные свойства доступа наследуются как для доступа, так и для доступ

и

8.12.2 [[GetProperty]] (P)

При вызове внутреннего метода O [[GetProperty]] с именем свойства P выполняются следующие шаги:

  • Пусть prop является результатом вызова внутреннего метода [[GetOwnProperty]] O с именем свойства P.

  • Если prop не является undefined, верните prop.

  • Пусть proto является значением внутреннего свойства [[Prototype]] для O.

  • Если proto имеет значение null, верните undefined.

  • Возвращает результат вызова внутреннего метода [[GetProperty]] прото с аргументом P.

Мой вывод

ECMA четко заявляет, что существует внутреннее свойство прототипа, что на самом деле не имеет смысла, если JavaScript по-прежнему будет иметь доступ к некоторому внутреннему свойству конструктора, чтобы получить доступ к тому, что уже существует. Кроме того, все, что можно проверить, указывает на то, что конструктор является свойством прототипа, а не наоборот.

Поэтому я бы сказал, что можно с уверенностью сказать, что так оно и работает.

Я также перечитываю вашу цитату из Jon Resig. Я думаю, что он имеет в виду the prototype is a property of the constructor - свойство прототипа, к которому мы можем напрямую обращаться, и что каждая функция имеет. Он также не очень специфичен здесь. Думаю, он просто хотел простое объяснение и не хотел сразу путать людей.