2008-11-22 3 views
697

В чем разница междуИспользование «прототипа» против «этого» в JavaScript?

var A = function() { 
    this.x = function() { 
     //do something 
    }; 
}; 

и

var A = function() { }; 
A.prototype.x = function() { 
    //do something 
}; 
+18

Связанный: [Определение методов через прототип vs с использованием этого в конструкторе - действительно разница в производительности?] (Http://stackoverflow.com/q/12180790/1048572) и хорошие ответы на дубликаты: [Объявление метода в конструкторе или на прототипе] (http://stackoverflow.com/a/9772864/1048572), [это против прототипа] (http: // stackoverflow.com/a/4386809/1048572) – Bergi 2012-10-01 17:59:07

+0

Концепция этого ключевого слова объясняется явно здесь https://scotch.io/@alZami/understanding-this-in-javascript – 2017-09-10 19:11:42

+0

Чтение «этой» темы показывает, насколько ужасен JS и насколько его принципы неясны для многих разработчиков. Что именно неправильно с более понятными языками? Я думаю, что настало время разработчики поднять свой голос, чтобы отказаться от запутывающих технологий, которые не имеют ни малейшего значения ни для бизнеса, ни для развития. – NoChance 2017-10-02 17:09:32

ответ

24

Первый пример изменяет интерфейс только для этого объекта. Второй пример изменяет интерфейс для всех объектов этого класса.

+0

Оба сделают функцию `x` доступной для всех объектов, прототипу которых назначен новый экземпляр A:` function B() {}; B.prototype = new A(); var b = new B(); b.x() // Вызовите A.x, если A определяется первым примером; ` – 2016-04-30 18:32:33

52

В большинстве случаев они по существу одинаковы, но вторая версия сохраняет память, поскольку для каждого объекта существует только один экземпляр функции, а не отдельная функция.

Основанием для использования первой формы является доступ к «частным членам». Например:

var A = function() { 
    var private_var = ...; 

    this.x = function() { 
     return private_var; 
    }; 

    this.setX = function (new_x) { 
     private_var = new_x; 
    }; 
}; 

Из-за правил области видимости в JavaScript, private_var доступен для функции, назначенной на this.x, но не за пределами объекта.

+1

См. это сообщение: http://stackoverflow.com/a/1441692/654708 для примера о том, как получить доступ к закрытым членам через прототипы. – GFoley83 2014-03-12 00:28:25

+0

@ GFoley83, который отвечает _not_, показывает, что - методы прототипа могут обращаться только к «общедоступным» свойствам данного объекта. Только привилегированные методы (а не прототип) могут получить доступ к закрытым членам. – Alnitak 2015-12-17 22:56:29

7

Я считаю, что @Matthew Crumley прав. Они функционально, если не структурно, эквивалентно. Если вы используете Firebug для просмотра объектов, созданных с помощью new, вы можете видеть, что они одинаковы. Однако, мое предпочтение было бы следующим. Я предполагаю, что это просто похоже на то, к чему я привык в C#/Java. То есть, определите класс, определите поля, конструктор и методы.

var A = function() {}; 
A.prototype = { 
    _instance_var: 0, 

    initialize: function(v) { this._instance_var = v; }, 

    x: function() { alert(this._instance_var); } 
}; 

EDIT Не имею в виду, что сфера применения переменной была частной, я просто пытался показать, как я определяю свои классы в JavaScript. Имя переменной изменено, чтобы отразить это.

+2

_instance_var, как в методах `initialize` и` x, не ссылаются на свойство `_instance_var` на экземпляр` A`, а на глобальный. Используйте `this._instance_var`, если вы хотите использовать свойство` _instance_var` экземпляра `A`. – Lekensteyn 2011-04-08 16:40:08

+0

@Lek - oops. исправлено. – tvanfosson 2011-04-08 17:46:27

+2

Самое смешное, что и у Бенри была такая ошибка, которая была обнаружена и через два года: p – Lekensteyn 2011-04-08 19:04:29

422

Примеры имеют очень разные результаты.

Прежде чем взглянуть на различия, следует отметить следующее:

  • Прототип конструктора обеспечивает способ совместного использования методов и ценностей среди экземпляров через частное [[Prototype]] имущества экземпляра.
  • Функция определяется как функция вызывается или используется bind (здесь не обсуждается). Когда функция вызывается на объект (например, myObj.method()), то этот в рамках метода ссылается на объект. Где это не установлено вызовом или использованием bind, оно по умолчанию является глобальным объектом (окно в браузере) или в строгом режиме, остается неопределенным.
  • JavaScript - это объектно-ориентированный язык, то есть все объекты, включая функции.

Так вот сниппеты в вопросе:

var A = function() { 
    this.x = function() { 
     //do something 
    }; 
}; 

В этом случае переменная A присваивается значение, которое является ссылкой на функцию.Когда эта функция вызывается с использованием A(), функция этого не задается вызовом, поэтому по умолчанию используется глобальный объект, и выражение this.x эффективно window.x. В результате ссылка на выражение функции с правой стороны назначается window.x.

В случае:

var A = function() { }; 
A.prototype.x = function() { 
    //do something 
}; 

что-то очень разные происходит. В первой строке переменной A присваивается ссылка на функцию. В JavaScript все объекты функций имеют свойство prototype по умолчанию, поэтому для создания объекта не существует отдельного кода. A.prototype.

Во второй строке A.prototype.x присваивается ссылка на функцию. Это создаст свойство x, если оно не существует, или назначьте новое значение, если это произойдет. Таким образом, разница с первым примером заключается в том, какое свойство объекта имеет значение x.

Другой пример приведен ниже. Это похоже на первое (и может быть, что вы хотели спросить о):

var A = new function() { 
    this.x = function() { 
     //do something 
    }; 
}; 

В этом примере оператор new был добавлен перед выражением функции, так что функция вызывается как конструктор. При вызове с new функция этого установлена ​​в качестве ссылки на новый объект, чье личное свойство [[Prototype]] установлено для ссылки на публичный конструктор прототипом. Поэтому в операторе присваивания на этом новом объекте будет создано свойство x. Когда вызывается как конструктор, функция возвращает свой этот объект по умолчанию, поэтому нет необходимости в отдельном заявлении return this;.

Чтобы проверить, что имеет свойство х:

console.log(A.x) // function() { 
       // //do something 
       // }; 

Это необычное использование нового, так как единственный способ ссылаться конструктор через A.constructor. Было бы гораздо больше общего, чтобы сделать:

var A = function() { 
    this.x = function() { 
     //do something 
    }; 
}; 
var a = new A(); 

Другой способ достижения подобного результата заключается в использовании сразу вызывается функция выражение:

var A = (function() { 
    this.x = function() { 
     //do something 
    }; 
}()); 

В этом случае A присваивается возвращаемое значение вызова функция справа. И здесь, начиная с , этот не задан в вызове, он будет ссылаться на глобальный объект, а this.x - фактически window.x. Так как функция ничего не возвращает, A будет иметь значение undefined.

Эти различия между двумя подходами также проявляются, если вы сериализуете и десериализуете свои объекты Javascript в/из JSON.Методы, определенные в прототипе объекта не сериализовать, когда вы сериализовать объект, который может быть удобно, если, например, вы хотите сериализовать только те части данных объекта, но не это методы:

var A = function() { 
    this.objectsOwnProperties = "are serialized"; 
}; 
A.prototype.prototypeProperties = "are NOT serialized"; 
var instance = new A(); 
console.log(instance.prototypeProperties); // "are NOT serialized" 
console.log(JSON.stringify(instance)); 
// {"objectsOwnProperties":"are serialized"} 

Похожие вопросы :

Sidenote: Там не может быть какой-либо существенная экономия памяти между этими двумя подходами, однако, используя прототип для обмена методов и свойства, вероятно, использовать меньше память, чем каждый экземпляр, имеющий свою собственную копию.

JavaScript не является языком низкого уровня. Возможно, не очень важно думать о прототипировании или других шаблонах наследования, чтобы явным образом изменить способ распределения памяти.

218

Как уже говорилось, первая версия, используя «это», приводит к каждому экземпляру класса A, имеющему собственную независимую копию метода функции «x». Принимая во внимание, что использование «прототипа» будет означать, что каждый экземпляр класса A будет использовать ту же копию метода «x».

Вот код, чтобы показать эту тонкую разницу:

// x is a method assigned to the object using "this" 
var A = function() { 
    this.x = function() { alert('A'); }; 
}; 
A.prototype.updateX = function(value) { 
    this.x = function() { alert(value); } 
}; 

var a1 = new A(); 
var a2 = new A(); 
a1.x(); // Displays 'A' 
a2.x(); // Also displays 'A' 
a1.updateX('Z'); 
a1.x(); // Displays 'Z' 
a2.x(); // Still displays 'A' 

// Here x is a method assigned to the object using "prototype" 
var B = function() { }; 
B.prototype.x = function() { alert('B'); }; 

B.prototype.updateX = function(value) { 
    B.prototype.x = function() { alert(value); } 
} 

var b1 = new B(); 
var b2 = new B(); 
b1.x(); // Displays 'B' 
b2.x(); // Also displays 'B' 
b1.updateX('Y'); 
b1.x(); // Displays 'Y' 
b2.x(); // Also displays 'Y' because by using prototype we have changed it for all instances 

Как уже упоминалось, существуют различные причины, чтобы выбрать один метод или другой. Мой образец просто предназначен, чтобы четко продемонстрировать разницу.

10

Прототип является шаблоном класса; который применяется ко всем будущим его экземплярам. В то время как это конкретный экземпляр объекта.

17

Конечная проблема с использованием this вместо prototype заключается в том, что при переопределении метода конструктор базового класса все равно будет ссылаться на переопределенный метод. Рассмотрим это:

BaseClass = function() { 
    var text = null; 

    this.setText = function(value) { 
     text = value + " BaseClass!"; 
    }; 

    this.getText = function() { 
     return text; 
    }; 

    this.setText("Hello"); // This always calls BaseClass.setText() 
}; 

SubClass = function() { 
    // setText is not overridden yet, 
    // so the constructor calls the superclass' method 
    BaseClass.call(this); 

    // Keeping a reference to the superclass' method 
    var super_setText = this.setText; 
    // Overriding 
    this.setText = function(value) { 
     super_setText.call(this, "SubClass says: " + value); 
    }; 
}; 
SubClass.prototype = new BaseClass(); 

var subClass = new SubClass(); 
console.log(subClass.getText()); // Hello BaseClass! 

subClass.setText("Hello"); // setText is already overridden 
console.log(subClass.getText()); // SubClass says: Hello BaseClass! 

против:

BaseClass = function() { 
    this.setText("Hello"); // This calls the overridden method 
}; 

BaseClass.prototype.setText = function(value) { 
    this.text = value + " BaseClass!"; 
}; 

BaseClass.prototype.getText = function() { 
    return this.text; 
}; 

SubClass = function() { 
    // setText is already overridden, so this works as expected 
    BaseClass.call(this); 
}; 
SubClass.prototype = new BaseClass(); 

SubClass.prototype.setText = function(value) { 
    BaseClass.prototype.setText.call(this, "SubClass says: " + value); 
}; 

var subClass = new SubClass(); 
console.log(subClass.getText()); // SubClass says: Hello BaseClass! 

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

var A = function (param1) { 
    var privateVar = null; // Private variable 

    // Calling this.setPrivateVar(param1) here would be an error 

    this.setPrivateVar = function (value) { 
     privateVar = value; 
     console.log("setPrivateVar value set to: " + value); 

     // param1 is still here, possible memory leak 
     console.log("setPrivateVar has param1: " + param1); 
    }; 

    // The constructor logic starts here possibly after 
    // many lines of code that define methods 

    this.setPrivateVar(param1); // This is valid 
}; 

var a = new A(0); 
// setPrivateVar value set to: 0 
// setPrivateVar has param1: 0 

a.setPrivateVar(1); 
//setPrivateVar value set to: 1 
//setPrivateVar has param1: 0 

против:

var A = function (param1) { 
    this.setPublicVar(param1); // This is valid 
}; 
A.prototype.setPublicVar = function (value) { 
    this.publicVar = value; // No private variable 
}; 

var a = new A(0); 
a.setPublicVar(1); 
console.log(a.publicVar); // 1 
13

В чем разница? => Много.

Я думаю, что версия this используется для включения инкапсуляции, т. Е. Скрытия данных. Это помогает манипулировать частными переменными.

Давайте посмотрим на следующем примере:

var AdultPerson = function() { 

    var age; 

    this.setAge = function(val) { 
    // some housekeeping 
    age = val >= 18 && val; 
    }; 

    this.getAge = function() { 
    return age; 
    }; 

    this.isValid = function() { 
    return !!age; 
    }; 
}; 

Теперь prototype структура может применяться следующим образом:

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

AdultPerson.prototype.getRights = function() { 
    // Should be valid 
    return this.isValid() && ['Booze', 'Drive']; 
}; 

Давайте посмотрим на реализацию сейчас.

var p1 = new AdultPerson; 
p1.setAge(12); // (age = false) 
console.log(p1.getRights()); // false (Kid alert!) 
p1.setAge(19); // (age = 19) 
console.log(p1.getRights()); // ['Booze', 'Drive'] (Welcome AdultPerson) 

var p2 = new AdultPerson; 
p2.setAge(45);  
console.log(p2.getRights()); // The same getRights() method, *** not a new copy of it *** 

Надеюсь, это поможет.

5

Как обсуждалось в других ответах, это действительно соображение производительности, поскольку функция в прототипе разделяется со всеми экземплярами, а не с создаваемой функцией для каждого экземпляра.

Я собрал jsperf, чтобы показать это. Существует огромное различие во времени, которое требуется для создания экземпляра класса, хотя это действительно актуально, если вы делаете много случаев.

http://jsperf.com/functions-in-constructor-vs-prototype

10

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

Большинство ответов упомянули разницу уже, то есть когда прототипирование функции разделяется на все (будущие) экземпляры. В то время как объявление функции в классе создаст копию для каждого экземпляра.

В целом, нет правильного или неправильного решения, это скорее вопрос вкуса или дизайнерское решение в зависимости от ваших требований. Прототипом, однако, является метод, который используется для развития объектно-ориентированным образом, как я надеюсь, вы увидите в конце этого ответа.

Вы показали два шаблона в своем вопросе. Я попытаюсь объяснить еще два и попытаться объяснить различия, если это уместно. Не стесняйтесь редактировать/продлевать. Во всех примерах речь идет о объекте автомобиля, который имеет местоположение и может двигаться.

декоратор Объект шаблон

Не уверен, что если эта модель по-прежнему актуальна в наши дни, но она существует. И это хорошо знать об этом. Вы просто передаете объект и свойство функции декоратора. Декоратор возвращает объект с помощью свойства и метода.

var carlike = function(obj, loc) { 
    obj.loc = loc; 
    obj.move = function() { 
     obj.loc++; 
    }; 
    return obj; 
}; 

var amy = carlike({}, 1); 
amy.move(); 
var ben = carlike({}, 9); 
ben.move(); 

Функциональные классы

функция в JavaScript является специализированным объектом. В дополнение к вызову функция может хранить свойства, подобные любому другому объекту.

В этом случае Car является функцией (также думает объекта), который может быть вызван, как вы привыкли делать. Он имеет свойство methods (это объект с функцией move).Когда Car вызывается функция extend называется, которая делает некоторые магии, и расширяет Car функции (думаю, объект) с методами, определенными в methods.

Этот пример, хотя и разные, ближе к первому примеру в вопросе.

var Car = function(loc) { 
    var obj = {loc: loc}; 
    extend(obj, Car.methods); 
    return obj; 
}; 

Car.methods = { 
    move : function() { 
     this.loc++; 
    } 
}; 

var amy = Car(1); 
amy.move(); 
var ben = Car(9); 
ben.move(); 

прототипичных Классы

Первых два шаблонов позволяют обсуждение использования методов для определения общих методов или с использованием методов, которые определены встроенными в теле конструктора. В обоих случаях каждый экземпляр имеет свою собственную функцию move.

прототипичная картина не поддается хорошо же обследование, потому что разделение функций с помощью делегации прототипа является самой целью для прототипного шаблона. Как отмечали другие, ожидается, что у него будет больше памяти.

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

В отношении последних три линии:

В этом примере Car ссылки на prototype объект, который связывает с помощью constructor к самому Car, т.е. Car.prototype.constructor является Car сам по себе. Это позволяет определить, какая функция конструктора построила определенный объект.

amy.constructor Поиск не удался и, следовательно, делегирован Car.prototype, который имеет свойство конструктора. И так amy.constructor - Car.

Кроме того, amy является instanceofCar. Оператор instanceof работает, видя, если правый операнд в прототипе объект (Car) можно найти в любом месте в прототипе левого операнда (amy) цепи.

var Car = function(loc) { 
    var obj = Object.create(Car.prototype); 
    obj.loc = loc; 
    return obj; 
}; 

Car.prototype.move = function() { 
     this.loc++; 
}; 

var amy = Car(1); 
amy.move(); 
var ben = Car(9); 
ben.move(); 

console.log(Car.prototype.constructor); 
console.log(amy.constructor); 
console.log(amy instanceof Car); 

Некоторые разработчики могут быть смущены в начале. Ниже пример:

var Dog = function() { 
    return {legs: 4, bark: alert}; 
}; 

var fido = Dog(); 
console.log(fido instanceof Dog); 

Оператор instanceof возвращает false, потому что Dog «прототип s не может быть найден в любом месте fido» цепи прототипов s. fido представляет собой простой объект, который создается с объектом буквального, то есть его просто делегаты Object.prototype.

псевдоклассическая модель

Это действительно просто другая форма прототипных модели в упрощенном виде и более знакомом делать тем, кто программировать на Java, например, так как она использует new конструктор.

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

Однако основное отличие заключается в том, что в механизмах JavaScript реализованы оптимизации, которые применяются только при использовании псевдоклассического шаблона. Подумайте о псевдоклассическом шаблоне, вероятно, более быстрой версии прототипа; объектные отношения в обоих примерах одинаковы.

var Car = function(loc) { 
    this.loc = loc; 
}; 

Car.prototype.move = function() { 
     this.loc++; 
}; 

var amy = new Car(1); 
amy.move(); 
var ben = new Car(9); 
ben.move(); 

Наконец, оно не должно быть слишком трудно понять, как объектно-ориентированное программирование может быть сделано. Есть два раздела.

Один раздел, который определяет общие свойства/методы в прототипе (цепочке).

И еще один раздел, где вы помещаете определения, которые отличают объекты друг от друга (переменная loc в примерах).

Это то, что позволяет применять такие понятия, как суперкласс или подкласс в JavaScript.

Не стесняйтесь добавлять или изменять. Еще раз, я мог бы сделать это сообщество вики, возможно.

101

Возьмите эти 2 примера:

var A = function() { this.hey = function() { alert('from A') } }; 

против

var A = function() {} 
A.prototype.hey = function() { alert('from prototype') }; 

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

a) Функция - это объект в JavaScript. Каждый объект в JavaScript получает внутреннее свойство (что означает, что вы не можете получить к нему доступ, как другие свойства, за исключением, может быть, в браузерах, таких как Chrome), часто называемых __proto__ (вы можете на самом деле ввести anyObject.__proto__ в Chrome, чтобы увидеть, на что он ссылается. просто это свойство, ничего более. Свойство в JavaScript = переменная внутри объекта, не более. Что делают переменные? Они указывают на вещи.

Так что же это свойство __proto__? Ну, обычно другое объект (мы объясним, почему позже). Единственный способ заставить JavaScript для свойства __proto__ НЕ указывать на другой объект - использовать var newObj = Object.create(null). Даже если вы сделаете это, свойство STILL __proto__ существует как свойство объекта, просто он не указывает на другой объект, он указывает на null.

Вот где большинство людей запутаться:

При создании новой функции в JavaScript (?, Который является объектом, а, помню), в тот момент он определен, JavaScript автоматически создает новое свойство на этой функции prototype. Попробуйте:

var A = []; 
A.prototype // undefined 
A = function() {} 
A.prototype // {} // got created when function() {} was defined 

A.prototype СОВЕРШЕННО РАЗНЫЕ от __proto__ собственности. В нашем примере «A» теперь имеет TWO-свойства, называемые «прототипом» и __proto__. Это большая путаница для людей. prototype и __proto__ свойства никоим образом не связаны друг с другом, это отдельные вещи, указывающие на отдельные значения.

Возможно, вы задаетесь вопросом: почему JavaScript имеет __proto__ свойство, созданное на каждом объекте? Ну, одно слово: делегация. Когда вы вызываете свойство объекта, а объект его не имеет, JavaScript ищет объект, на который ссылается __proto__, чтобы узнать, может ли он быть. Если он его не имеет, то он смотрит на свойство объекта __proto__ и так далее ... пока цепь не закончится. Таким образом, имя прототипа . Разумеется, если __proto__ не указывает на объект и вместо этого указывает на null, это жуткий случай, JavaScript реализует это и вернет вам undefined.

Вы также можете задаться вопросом, почему JavaScript создает свойство, называемое prototype для функции при определении функции? Поскольку он пытается обмануть вас, да обмануть вас, что он работает как языки на основе классов.

Давайте вернемся к нашему примеру и создать «объект» из A:

var a1 = new A(); 

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

Тот факт, что вы использовали оператор new перед вызовом функции A() сделал что-то ДОПОЛНИТЕЛЬНОЕ в фоновом режиме. Ключевое слово new создало новый объект, который теперь ссылается на a1, и этот объект пуст. Вот что происходит дополнительно:

Мы сказали, что при определении каждой функции создается новое свойство, созданное с именем prototype (которое вы можете получить к нему, в отличие от свойства __proto__)? Ну, это свойство используется сейчас.

Итак, теперь мы находимся в точке, где у нас есть свежеиспеченный пустой объект a1. Мы сказали, что все объекты в JavaScript имеют внутреннее свойство __proto__, которое указывает на что-то (a1 также имеет его), является ли он нулевым или другим объектом. Что делает оператор new, так это то, что он устанавливает свойство __proto__, чтобы указать на свойство функции prototype. Прочтите это снова. Это в основном это:

a1.__proto__ = A.prototype; 

Мы сказали, что A.prototype не более чем пустой объект (если мы не изменим его к чему-то еще до определения a1). Итак, теперь в основном a1.__proto__ указывает на то же самое A.prototype указывает на то, что это пустой объект. Они указывают на один и тот же объект, который был создан, когда произошло это линия:

A = function() {} // JS: cool. let's also create A.prototype pointing to empty {} 

Теперь, есть еще одна вещь, что происходит, когда обрабатывается var a1 = new A() заявление. В основном A() выполняется, и если А это что-то вроде этого:

var A = function() { this.hey = function() { alert('from A') } }; 

Все, что материал внутри function() { } собирается выполнить.Когда вы достигнете this.hey.. линии, this изменяется на a1 и вы получите это:

a1.hey = function() { alert('from A') } 

Я не буду почему this изменения a1 но this is a great answer, чтобы узнать больше.

Итак, подведем итог, когда вы делаете `` вар a1 = новый A() `есть 3 вещи, которые происходят в фоновом режиме:

  1. Совершенно новый пустой объект создается и присвоенные a1. a1 = {}
  2. a1.__proto__ свойство присваивается точке, в одно и то же, как A.prototype указывает на (другой пустой объект {})

  3. Функция A() в настоящее время выполняется с this набора на новый пустой объект, созданный на шаге 1, (читайте ответ, который я ссылаюсь выше, почему this изменений a1)

Теперь, давайте попробуем создать еще один объект:

var a2 = new A(); 

Этапы 1,2,3 повторятся. Вы что-то замечаете? Ключевое слово: . Шаг 1: a2 будет новый пустой объект, шаг 2: его свойство __proto__ будет указывать на то же самое A.prototype указывает на и, самое главное, шаг 3: функция A() снова выполняется, это означает, что a2 получит hey собственность, содержащий функция. a1 и a2 имеют два свойства SEPARATE с именем hey, которые указывают на 2 ОТДЕЛЬНЫХ функции! Теперь у нас есть повторяющиеся функции в тех же двух разных объектах, которые делают одно и то же, oops ... Вы можете представить себе последствия памяти для этого, если у нас есть 1000 объектов, созданных с помощью new A, после того, как все объявления функций занимают больше памяти, чем что-то вроде номера 2. Итак, как мы можем предотвратить это?

Помните, почему свойство __proto__ существует на каждом объекте? Таким образом, если вы вернете свойство yoMan на a1 (которого не существует), будет рассмотрено его свойство __proto__, которое, если оно является объектом (и в большинстве случаев оно), проверяет, содержит ли оно yoMan, и если оно не будет, он будет консультироваться с этим объектом __proto__ и т. д. Если это произойдет, оно примет значение этого свойства и отобразит его для вас.

Так кто-то решил использовать этот факт + тот факт, что при создании a1, его __proto__ свойство указывает на то же (пустой) объект A.prototype указывает на и сделать это:

var A = function() {} 
A.prototype.hey = function() { alert('from prototype') }; 

Круто! Теперь, когда вы создаете a1, он снова проходит все три шага выше, а на шаге 3 ничего не делает, поскольку function A() ничего не может выполнить.И если мы делаем:

a1.hey 

Он будет видеть, что a1 не содержит hey и он будет проверять свой объект __proto__ свойства, чтобы увидеть, если он имеет его, что это так.

При таком подходе мы исключаем часть из шага 3, где функции дублируются при создании каждого нового объекта. Вместо a1 и a2, имеющих отдельное имущество hey, теперь у НИЖЕ их нет. Которая, я думаю, вы уже поняли. Это хорошая вещь ... если вы поймете __proto__ и Function.prototype, вопросы, подобные этим, будут довольно очевидны.

ПРИМЕЧАНИЕ. Некоторые люди обычно не называют внутреннее свойство прототипа __proto__. Я использовал это имя через сообщение, чтобы четко определить его свойство Functional.prototype как две разные вещи.

14

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

Свойство конструктора функций ссылается на объект-прототип всех экземпляров, созданных с помощью этой функции, при использовании new.


В первом примере, вы добавляете свойство x для каждого экземпляра, созданного с помощью функции A.

var A = function() { 
    this.x = function() { 
     //do something 
    }; 
}; 

var a = new A(); // constructor function gets executed 
        // newly created object gets an 'x' property 
        // which is a function 
a.x();    // and can be called like this 

Во втором примере вы добавляете свойство к объекту-прототипу, что все экземпляры, созданные с A точки до.

var A = function() { }; 
A.prototype.x = function() { 
    //do something 
}; 

var a = new A(); // constructor function gets executed 
        // which does nothing in this example 

a.x();    // you are trying to access the 'x' property of an instance of 'A' 
        // which does not exist 
        // so JavaScript looks for that property in the prototype object 
        // that was defined using the 'prototype' property of the constructor 

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

6

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

Function directly on object

Function on prototype

Здесь мы создаем 2000000 новые объекты с методом print в Chrome. Мы сохраняем каждый объект в массиве. Помещение print на прототип занимает около 1/2.

1

Подумайте о статически типизированном языке, вещи на prototype являются статическими, а вещи на this связаны с экземплярами.

Смежные вопросы