Есть ли разница между функцией конструктора и прототипом объекта при использовании наследования?



Рассмотрим следующие фрагменты кода 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, но в чем разница? Зачем мне делать это в разных местах?

322   4  

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, хотя обе функции выполняют в точности то же самое.

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

В конце концов, это" просто " наследование, которое является одним из понятий в разработке программного обеспечения (например, агрегация) и может использоваться везде, где это имеет смысл. Ваш второй фрагмент в основном означает, что a baz это-а 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

    Ничего не найдено.