Прототипы Javascript и создание экземпляров

Я извиняюсь, потому что эта тема очень много, но я не смог адекватно объяснить это во всем, что я читал сегодня.

Я пытаюсь создать простой класс коллекции (и узнать о прототипах javascript в то же время), предназначенный для хранения объектов с свойством "name" и позволяет доступ к его членам по индексу или значению. Пока у меня есть это:

function Collection() {}
Collection.prototype.constructor = Collection;
Collection.prototype._innerList = [];
Collection.prototype._xref = {};
Collection.prototype.count = function () { return this._innerList.length; };
Collection.prototype.add = function (obj) {
    this._xref[obj.name] = this._innerList.push(obj) - 1;
}
Collection.prototype.get = function (id) {
    if (typeof id == "string") {
        return this._innerList[this._xref[id]];
    } else {
        return this._innerList[id];
    }
};

http://jsfiddle.net/YrVFZ/

Проблема:

var foo = new Collection();
foo.add({name: "someitem", value:"hello world"});   // foo.count()== 1

var bar= new Collection();
bar.add({name: "someotheritem", value:"hello world"}); // bar.count()== 2

Хм...

В принципе, новый экземпляр bar создается со всеми свойствами, имеющими текущие значения данных в foo. Я знаю, что могу исправить это, поставив _xref и т.д. Внутри конструктора, но я пытаюсь понять, как работает прототипирование. Если я создаю новый экземпляр и вношу изменения в данные в этом экземпляре, почему эти значения переносятся при создании другого нового экземпляра?

Если я внесу дальнейшие изменения в свойство из прототипа foo или bar, они независимы, поэтому не похоже, что я каким-то образом ссылаюсь на один и тот же экземпляр чего-либо. Итак, что вызывает bar для создания экземпляров с текущими значениями из foo?

Ответ 1

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

Collection.prototype._innerList = [];

вы даете каждой коллекции это свойство; независимо от вызова new Collection() любые изменения на доске затрагивают всех учащихся. Однако, если вы определяете его внутри конструктора или одну из функций как this.variableName = [], каждая копия будет иметь собственное имя переменной, например, раздавать каждому ученику раздаточный материал. Очевидно, что в некоторых случаях, когда есть что-то на белой доске, например, инструкции, которые будут универсальны от ученика к ученику, но если каждый предмет будет отличаться для каждого ученика, он должен быть индивидуальным. Надеюсь, что это объяснение имеет смысл...

Вы хотите сделать это.

function Collection() {
    if (!this instanceof Collection)
        return new Collection();
    this._innerList = [];
    this._xref = {};

    return this;
}

Collection.prototype.count = function() {
    return this._innerList.length;
};
Collection.prototype.add = function(obj) {
    this._xref[obj.name] = this._innerList.push(obj) - 1;
}
Collection.prototype.get = function(id) {
    if (typeof id == "string") {
        return this._innerList[this._xref[id]];
    } else {
        return this._innerList[id];
    }
};

var foo = new Collection();
foo.add({name: "someitem", value:"hello world"});   
console.log(foo.count()); // 1

var bar= new Collection();
bar.add({name: "someotheritem", value:"hello world"});
console.log(bar.count()); // 1

http://jsfiddle.net/vXbLL/

Изменить

Не имеет значения для вашего вопроса, но это то, что я делаю, поэтому я его выброшу. Всякий раз, когда я что-то делаю на прототипе, если я ничего не возвращаю, я возвращаю this. Он позволяет цепочки, поэтому вы можете сделать instance.function1().function2().function3(), пока function1 и function2 return this.

Ответ 2

Вы можете думать о прототипе как о предоставлении всех объектов общих переменных этого класса. Как статические переменные в классе С++, если это имеет смысл. Это нормально делать для функций, потому что они одинаковы для каждого экземпляра класса. Однако, если вы хотите, чтобы объект имел свою собственную не общую переменную, вы не должны использовать прототип. Один простой способ сделать это - назначить их в методе конструктора следующим образом:

function Collection() 
{
this._innerList = [];
this._xref = {};
}

Collection.prototype.count = function () { return this._innerList.length; };
Collection.prototype.add = function (obj) {
    this._xref[obj.name] = this._innerList.push(obj) - 1;
}
Collection.prototype.get = function (id) {
    if (typeof id == "string") {
        return this._innerList[this._xref[id]];
    } else {
        return this._innerList[id];
    }
};

var foo = new Collection();
foo.add({name: "someitem", value:"hello world"});   // foo.count()== 1
document.write(foo.count(),"<br>");

var bar= new Collection();
bar.add({name: "someotheritem", value:"hello world"}); // bar.cou
document.write(bar.count(),"<br>");