Javascript прототипы и создание экземпляров - PullRequest
13 голосов
/ 30 апреля 2011

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

Я пытаюсь создать простой класс коллекции (и в то же время узнать о прототипировании 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?

Ответы [ 2 ]

22 голосов
/ 30 апреля 2011

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

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 вернут this.

5 голосов
/ 30 апреля 2011

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

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>");
...