Ответ 1
Экземпляры, созданные функцией конструктора (в вашем случае это clog
), наследуют ссылку на объект clog.prototype
. Поэтому, если вы добавите свойство в clog.prototype
, оно будет отображаться в экземплярах. Если вы добавите свойство, чтобы clog
себя, оно не будет отображаться на экземплярах.
У вашего кода в кавычках есть некоторые проблемы, поэтому давайте рассмотрим абстрактный пример:
function Foo() {
}
Foo.prototype.bar = "I'm bar on Foo.prototype";
Foo.bar = "I'm bar on Foo";
var f = new Foo();
console.log(f.bar); // "I'm bar on Foo.prototype"
// E.g., 'f' inherits from 'Foo.prototype', not 'Foo'
// And this link is live, so:
Foo.prototype.charlie = "I'm charlie on Foo.prototype";
console.log(f.charlie); // "I'm charlie on Foo.prototype";
Из вашего комментария ниже:
Я не понимаю, почему новые свойства, добавленные непосредственно в
Foo
, будут игнорироваться цепочкой прототипов?
Поскольку это Foo.prototype
, а не Foo
, это прототип для объектов, созданных с помощью new Foo()
.
не
prototype
просто указывает на объект конструктора?
Нет, Foo
и Foo.prototype
- это совершенно разные объекты. Foo
- это функциональный объект, который, как и все функциональные объекты, может иметь свойства. Одним из свойств Foo
является prototype
, который является нефункциональным объектом, который изначально пуст, кроме свойства constructor
которое указывает на Foo
. Именно Foo.prototype
, а не Foo
, экземпляры, созданные с помощью new Foo
получают в качестве своего прототипа. Единственная роль Foo
заключается в создании объектов, которые используют Foo.prototype
качестве своего прототипа. (На самом деле, в случае Foo
, он просто инициализирует эти объекты; они создаются оператором new
. С традиционной функцией, такой как Foo
, new
создает объект. Если бы этот код использовал синтаксис class
ES2015+, new
не создавал бы объект, он оставил бы, чтобы Foo
[если Foo
были базовый класс конструктора] или Foo
конечного базового класса, если [ Foo
был конструктором подкласса].)
Если я делаю
Foo.newProp = "new addition"
почемуf.newProp => undefined
?
(Чтобы избежать путаницы, я изменил Foo.new =...
на Foo.newProp =...
выше, поскольку new
- это ключевое слово. Хотя вы можете использовать его так же, как в ES5, лучше этого не делать.)
Потому что Foo.newProp
практически не имеет ничего общего с f
. Вы можете найти его в f.constructor.newProp
, так как f.constructor
это Foo
.
Некоторые ASCII-арт:
Учитывая этот код:
function Foo() {
}
Foo.prototype.bar = "I'm bar on Foo.prototype";
Foo.bar = "I'm bar on Foo";
у нас есть эти объекты со следующими свойствами (некоторые для ясности опущены):
+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+ | | V +−−−−−−−−−−−−−−−−−−+ | +−−−−−−−−−−−−−−−−+ +−−>| [String] | | | Foo [Function] | | +−−−−−−−−−−−−−−−−−−+ | +−−−−−−−−−−−−−−−−+ | | "I'm bar on Foo" | | | bar |−−−−+ +−−−−−−−−−−−−−−−−−−+ | | prototype |−−−−+ | +−−−−−−−−−−−−−−−−+ | | +−−−−−−−−−−+ | | | V | +−−−−−−−−−−−−−+ | | [Object] | | +−−−−−−−−−−−−−+ | | constructor |−−+ +−−−−−−−−−−−−−−−−−−−−−−−−−−−−+ | bar |−−−−−>| [String] | +−−−−−−−−−−−−−+ +−−−−−−−−−−−−−−−−−−−−−−−−−−−−+ | "I'm bar on Foo.prototype" | +−−−−−−−−−−−−−−−−−−−−−−−−−−−−+
Теперь, если мы сделаем
var f = new Foo();
у нас (новый материал выделен жирным шрифтом):
+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+ | | V +−−−−−−−−−−−−−−−−−−+ | +−−−−−−−−−−−−−−−−+ +−−>| [String] | | | Foo [Function] | | +−−−−−−−−−−−−−−−−−−+ | +−−−−−−−−−−−−−−−−+ | | "I'm bar on Foo" | | | bar |−−−−+ +−−−−−−−−−−−−−−−−−−+ | | prototype |−−−−+ | +−−−−−−−−−−−−−−−−+ | | +−−−−−−−−−−−−−+ | | | V | +−−−−−−−−−−−−−−−+ +−−−−−−−−−−−−−+ | | f [Object] | +−−−−−>| [Object] | | +−−−−−−−−−−−−−−−+ | +−−−−−−−−−−−−−+ | | [[Prototype]] |−−−−−−−−−−+ | constructor |−−+ +−−−−−−−−−−−−−−−−−−−−−−−−−−−−+ +−−−−−−−−−−−−−−−+ | bar |−−−−>| [String] | +−−−−−−−−−−−−−+ +−−−−−−−−−−−−−−−−−−−−−−−−−−−−+ | "I'm bar on Foo.prototype" | +−−−−−−−−−−−−−−−−−−−−−−−−−−−−+
([[Prototype]] является внутренним полем объекта, ссылающимся на его прототип. Это доступно через Object.getPrototypeOf
[или __proto__
в движках JavaScript в веб-браузерах, но не используйте __proto__
, оно только для обратной совместимости со старыми специфичными для SpiderMonkey код.)
Теперь предположим, что мы делаем это:
f.charlie = "I'm charlie on f";
Все, что изменяется, это объект f
(новый материал выделен жирным шрифтом):
+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+ | | V +−−−−−−−−−−−−−−−−−−+ | +−−−−−−−−−−−−−−−−+ +−−>| [String] | | | Foo [Function] | | +−−−−−−−−−−−−−−−−−−+ | +−−−−−−−−−−−−−−−−+ | | "I'm bar on Foo" | | | bar |−−−−+ +−−−−−−−−−−−−−−−−−−+ | | prototype |−−−−+ | +−−−−−−−−−−−−−−−−+ | | +−−−−−−−−−−−−−+ | | | V | +−−−−−−−−−−−−−−−+ +−−−−−−−−−−−−−+ | | f [Object] | +−−−−−>| [Object] | | +−−−−−−−−−−−−−−−+ | +−−−−−−−−−−−−−+ | | [[Prototype]] |−−−−−−−−−−+ | constructor |−−+ +−−−−−−−−−−−−−−−−−−−−−−−−−−−−+ | charlie |−−−−−−−−−−+ | bar |−−−−−>| [String] | +−−−−−−−−−−−−−−−+ | +−−−−−−−−−−−−−+ +−−−−−−−−−−−−−−−−−−−−−−−−−−−−+ | | "I'm bar on Foo.prototype" | | +−−−−−−−−−−−−−−−−−−−−−−−−−−−−+ | | +−−−−−−−−−−−−−−−−−−−−+ +−−−−−>| [String] | +−−−−−−−−−−−−−−−−−−−−+ | "I'm charlie on f" | +−−−−−−−−−−−−−−−−−−−−+
Теперь у f
есть собственное свойство, которое называется charlie
. Это означает, что эти два утверждения:
console.log(f.charlie); // "I'm charlie on f"
console.log(f.bar); // "I'm bar on Foo.prototype"
Получите обработанный немного по-другому.
Давай сначала посмотрим на f.charlie
. Вот что делает двигатель с f.charlie
:
- Есть ли у
f
собственное свойство, называемое"charlie"
? - Да; использовать значение этого свойства.
Достаточно просто. Теперь давайте посмотрим, как двигатель обрабатывает f.bar
:
- Есть ли у
f
свое собственное свойство, называемое"bar"
? - Нет; это
f
есть прототип? - Да; делает
f
прототип имеет свойство под названием"bar"
? - Да; использовать значение этого свойства.
Таким образом, между f.charlie
и f.bar
существует большая разница: у f
есть собственное свойство charlie
, но унаследованное свойство bar
. А если f
объект - прототип не имел свойство называется bar
, его прототип объект (в данном случае, Object.prototype
) будет проверяться, и так далее по цепочке, пока не кончатся прототипы.
Кстати, вы можете проверить, является ли свойство "собственным" свойством, используя функцию hasOwnProperty
которая есть у всех объектов:
console.log(f.hasOwnProperty("charlie")); // true
console.log(f.hasOwnProperty("bar")); // false
Отвечая на ваш вопрос из комментариев:
Я делаю
function Person(first_name, last_name) {this.first_name = first_name; this.last_name = last_name;}
function Person(first_name, last_name) {this.first_name = first_name; this.last_name = last_name;}
а затемvar ilya = new Person('ilya', 'D')
как он разрешает свойства внутреннегоname
?
При вызове Person
этой части new Person(...)
выражения new Person(...)
this
относится к вновь созданному объекту, который будет возвращен new
выражением. Поэтому, когда вы делаете это this.prop = "value";
вы помещаете свойство непосредственно в этот объект, не имея ничего общего с прототипом.
Полагая, что еще один способ, эти два примера привести к точно такому же p
объекта:
// Example 1:
function Person(name) {
this.name = name;
}
var p = new Person("Fred");
// Example 2:
function Person() {
}
var p = new Person();
p.name = "Fred";
Вот проблемы с цитируемым кодом, который я упомянул:
Проблема 1: Возврат чего-либо из функции конструктора:
function clog(x){
var text = x;
return console.log(text ); // <=== here
}
В 99,9999% случаев вы не хотите возвращать что-либо из функции конструктора. new
операция работает следующим образом:
- Новый пустой объект создан.
- Ему присваивается прототип из свойства
prototype
конструктора. - Конструктор вызывается так, что
this
относится к новому объекту. - Если конструктор ничего не возвращает или возвращает что-то, кроме объекта, результатом
new
выражения будет объект, созданный на шаге 1. - Если функция конструктора возвращает объект, результатом
new
операции будет этот объект.
Так что в вашем случае, так как console.log
ничего не возвращает, вы просто удаляете ключевое слово return
из своего кода. Но если вы использовали это, return xyz();
создать с функцией, которая вернула объект, вы бы испортили вашу функцию конструктора.
Проблема 2: вызов функций, а не ссылки на них
В этом коде:
clog.prototype.alert = alert(text);
Вы вызываете функцию alert
и присваиваете результат ее свойству, называемому alert
о clog.prototype
. Так как alert
ничего не возвращает, он в точности эквивалентен:
alert(text);
clog.prototype.alert = undefined;
... что, вероятно, не то, что вы имели в виду. Может быть:
clog.prototype.alert = function(text) {
alert(text);
};
Там мы создаем функцию и присваиваем ссылку на нее свойству alert
в прототипе. Когда функция вызывается, она вызывает стандартное alert
.
Проблема 3: функции конструктора должны быть изначально ограничены
Это просто стиль, но он в подавляющем большинстве стандартен: функции конструктора (функции, предназначенные для использования с new
) должны начинаться с заглавной буквы, поэтому Clog
а не clog
. Опять же, это просто стиль.