Есть ли разница между функцией конструктора и прототипом объекта при использовании наследования?
Рассмотрим следующие фрагменты кода JavaScript:
function foo() {
this.bar = function() { };
}
// or... (if we used an empty constructor function)
foo.prototype.bar = function() { };
Какая разница, когда я делаю это:
function baz() {
}
baz.prototype = new foo();
В обоих случаях baz заканчивается тем, что у него есть член bar, но в чем разница? Зачем мне делать это в разных местах?
4 ответов:
Отличие состоит в том, чтогде в цепи прототипов находится свойство.
Предполагая, что у нас есть
f = new foo();иb = new baz(). Тогда мы имеем следующие ситуации:Определение
fooбез использования прототипа:+-----------+ +---------------+ | f | | foo.prototype | | __proto__-+---->| constructor | | bar | | | +-----------+ +---------------+
barявляется свойством самого объекта (f.howOwnProperty('bar')возвращаетtrue).Если вместо этого присвоить свойство прототипу, то ситуация выглядит следующим образом:
+-----------+ +---------------+ | f | | foo.prototype | | __proto__-+---->| constructor | | | | bar | +-----------+ +---------------+
fне имеет своего собственного свойстваbar, но свойство есть совместно используется со всеми другими экземплярамиfoo.Аналогично для второго фрагмента, который приводит либо к
+-----------+ +---------------+ +---------------+ | b | | foo instance | | foo.prototype | | __proto__-+---->| __proto__ -+---->| constructor | | | | bar | | | +-----------+ +---------------+ +---------------+Или
+-----------+ +---------------+ +---------------+ | b | | foo instance | | foo.prototype | | __proto__-+---->| __proto__ -+---->| constructor | | | | | | bar | +-----------+ +---------------+ +---------------+
Почему ты хочешь это сделать?
В основном речь идет о структуре, а не о потере памяти. Вы можете добавить функции к объекту в функции конструктора:
Но это также означает, что каждый экземплярfunction Foo() { this.bar = function() {}; }Fooимеет свою собственную функцию, то естьf1.bar === f2.barявляетсяfalse, хотя обе функции выполняют в точности то же самое.Использование прототипа дает вам чистый способ разделения свойств, общих для всех экземпляров и специфичных для каждого экземпляра.
В конце концов, это" просто " наследование, которое является одним из понятий в разработке программного обеспечения (например, агрегация) и может использоваться везде, где это имеет смысл. Ваш второй фрагмент в основном означает, что abazэто-аfoo, таким образом, экземплярbaz, помимо своих собственных свойств, обладает теми же свойствами, что и экземплярfoo(унаследованный).
Одно большое отличие состоит в том, что при изменении свойств прототипа эти изменения будут применяться ко всем экземплярам, включая уже существующие, в то время как при изменении свойства, созданного в конструкторе, оно будет изменено только для экземпляра, на котором оно было изменено.
Относительно того, что некоторые другие ответы говорили о настройке
barв конструкторе, в результате чего каждый экземпляр имеет свою собственную копию функции: это верно, если у вас есть выражение функции внутри конструктора. конструктор, как показано в коде в вопросе, но не верно, если вы назначаете ссылку на функцию, как это:В этом случае все экземпляры будут иметь свойствоfunction myFunction() {} function foo() { this.bar = myFunction; }bar, которое относится к одной и той же функции, но отдельный экземпляр все равно может иметь свое свойствоbar, присвоенное чему - то другому, не затрагивая другие экземпляры.
Чтобы добавить к существующим ответам:
Создание функции-прототипа позволит наследовать изменения в ней. то есть, если вы пишете
function foo(){} foo.prototype.bar = function(){return 1}; function baz(){} baz.prototype = new foo(); new baz().bar(); //returns 1 foo.prototype.bar = function(){return 2}; new baz().bar(); //returns 2Однако, помещая его в конструктор, другие объекты, наследующие от него, также "имеют" эту функцию, но функция не наследуется.
function foo(){this.bar = function(){return 1};} function baz(){} baz.prototype = new foo(); new baz().bar(); //returns 1 foo.prototype.bar = function(){return 2}; new baz().bar(); //returns 1
Я думаю, что отвечаю на правильный вопрос, иначе дайте мне знать.
Разница в том, что использование прототипа приводит только к одному экземпляру. Так, например, баз.бар в одном случае то же самое, что и баз.бар в другом. Они будут разделять значения в экземпляре 'foo':function foo() { var x = 0; this.bar = function() {}; this.getVal = function() { return x; } this.setVal = function(val) { x = val; } } function baz() {} baz.prototype = new foo(); var a = new baz(), b = new baz(); a.setVal("1234") console.log(b.getVal()); // prints '1234'Http://jsfiddle.net/jonathon/7GtRD/
Если бы
aиbнапрямую вызывали "foo", то они не разделяли бы значения внутриfoo. Однако именно здесь он немного отличается между настройкойthis.barи использованием прототипа для создания бара.Используя прототип, можно создать один экземпляр
bar. Так чтоa.barбудет то же самое, что иb.bar. Если вы сделаете это по-другому, то это будут две разные функции (делать одно и то же).
Comments