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

Добавление новых свойств в конструкторскую функцию без .prototype

когда у меня есть функция, которую я хочу использовать в качестве конструктора, скажем:

function clog(x){
    var text = x;
    return console.log(text );
}

И я сделал несколько его примеров

var bla = new clog();

Теперь я хочу добавить новую функциональность, поэтому я бы использовал

clog.prototype.alert = alert(text);

какая разница, если я просто сделаю:

clog.alert = alert(text);

Не будет ли это унаследовано объектами, которые clog являются их прототипом?

4b9b3361

Ответ 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:

  1. Есть ли у f собственное свойство, называемое "charlie"?
  2. Да; использовать значение этого свойства.

Достаточно просто. Теперь давайте посмотрим, как двигатель обрабатывает f.bar:

  1. Есть ли у f свое собственное свойство, называемое "bar"?
  2. Нет; это f есть прототип?
  3. Да; делает f прототип имеет свойство под названием "bar"?
  4. Да; использовать значение этого свойства.

Таким образом, между 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 операция работает следующим образом:

  1. Новый пустой объект создан.
  2. Ему присваивается прототип из свойства prototype конструктора.
  3. Конструктор вызывается так, что this относится к новому объекту.
  4. Если конструктор ничего не возвращает или возвращает что-то, кроме объекта, результатом new выражения будет объект, созданный на шаге 1.
  5. Если функция конструктора возвращает объект, результатом 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. Опять же, это просто стиль.

Ответ 2

Добавление функции clog.alert просто привязывает статическую функцию к объекту clog. Он не будет унаследован и не будет иметь доступ к экземпляру, созданному с помощью new clog(); в функции предупреждения.

Добавление clog.prototype.alert сделает объект new clog();, который вы создадите, наследует функцию, и вы также получите доступ к экземпляру внутри, используя ключевое слово this.

function John() {
    this.id = 1;
}

John.doe = function() {
  console.log(this);
  console.log(this.id); // undefined
}

John.prototype.doe = function() {
    console.log(this);
};

John.doe(); // the John object

var me = new John();
me.doe(); // the instance, inherited from prototype
console.log(me.id); // 1

Ответ 3

Любое свойство, добавленное в конструктор, будет действовать как статическое свойство, к которому можно получить доступ, обратившись к объекту-конструктору (т.е. к функции) и не используя для него какой-либо объект-экземпляр. Его как свойство класса, а не свойство экземпляра.