#javascript #inheritance
#javascript #наследование
Вопрос:
У меня есть следующая иерархия классов:
- Хранение
- Коллекция
- EixoCollection
- OfertaCollection
- Коллекция
И у меня есть следующий код:
var ofertaCollection = new OfertaCollection();
var eixoCollection = new EixoCollection();
ofertaCollection.set('mykey', 'myvalue');
alert(eixoCollection.get('mykey')); // it returns 'myvalue', should return nothing
Проблема в том, что ofertaCollection и eixoCollection имеют свойства, ссылающиеся друг на друга.
Следите за классами:
/**
* Storage
*
* @returns {Storage}
*/
function Storage(){
this.storage = []; // itens that have a key
// sets key
this.set = function(key, value){
this.storage[key] = value;
}
// gets key
this.get = function(key){
return this.storage[key];
}
}
/**
* Collection
*
* @returns {Collection}
*/
function Collection(){
}
Collection.prototype = new Storage();
/**
* EixoCollection
*
* @returns {EixoCollection}
*/
function EixoCollection(){
}
EixoCollection.prototype = new Collection();
/**
* OfertaCollection
*
* @returns {OfertaCollection}
*/
function OfertaCollection(){
}
OfertaCollection.prototype = new Collection();
В чем проблема?
Комментарии:
1. Зачем вам вообще этот
Collection
класс? Для меня это выглядит как серьезное раздувание… все, что вы сделали, это обернулиStorage
класс.
Ответ №1:
Во-первых, я предлагаю два изменения в вашем коде (которые будут использоваться в примерах): this.storage
должен быть объект {}
, поскольку похоже, что вы никогда не используете его как массив. Кроме того, get
и set
должны быть в прототипе, иначе для каждого объекта экземпляра будет создан новый экземпляр этих методов (если только умный компилятор не оптимизирует их, но мы не будем предполагать этого).
Решение 1. вы могли бы выполнить квазинаследование, при котором наследующим классам передаются только ваши методы хранилища, но не объект хранилища:
function Storage(){}
storage.prototype = {
get: function(key){
return this.storage[key];
},
set: function(key, value){
this.storage[key] = value;
}
};
function Collection(){
this.storage = {};
}
Collection.prototype = Storage.prototype; // quasi-inheritance
function EixoCollection(){}
EixoCollection.prototype = new Collection();
function OfertaCollection(){}
OfertaCollection.prototype = new Collection();
var ofertaCollection = new OfertaCollection();
var eixoCollection = new EixoCollection();
ofertaCollection.set('mykey', 'myvalue');
alert(eixoCollection.get('mykey')); // undefined
Решение 2: Реальное наследование, но для каждой коллекции создается экземпляр нового хранилища, чтобы во время поиска прототипа функция находила локальное хранилище. Это идентично описанному выше, но наследование будет таким же, как и раньше. Поэтому я просто заменю строку, которая отличается:
Collection.prototype = new Storage(); // real inheritance
Проблема с обеими этими реализациями заключается в том, что они требуют, чтобы наследующий класс выполнял две вещи: как наследовал методы, так и создавал экземпляр хранилища. Не очень.
Простой альтернативой и, возможно, наиболее интуитивно понятной является использование Storage
составного объекта, а не унаследованного. Коллекция имеет внутреннее хранилище и некоторые дополнительные функциональные возможности, поэтому она выполняет has- мнемоническую функцию, что делает композицию допустимым кандидатом.
Ответ №2:
Проблема в том, что оба объекта вашей коллекции совместно используют один и тот же Storage
объект.
var c1 = new OfteraCollection().prototype.prototype;
var c2 = new ExioCollection().prototype.prototype;
console.log(c1 === c2); // true
Чтобы исправить это, вы могли бы удалить Collection
все вместе и заставить каждый из объектов вашей коллекции создать свой собственный Storage
в качестве прототипа.
Комментарии:
1. Я исправил свой ответ, но мне все еще любопытно. Зачем вы это делаете? Есть ли у вас другие методы для ваших объектов? Из того, что я вижу, все, что вам действительно нужно сделать, это сказать
var coll = new Object(); coll[key] = value;
2. Да, у меня есть. Я показываю этот способ только для минимизации кода =). И я не понимаю исправленный ответ, не могли бы вы привести пример? (новичок в js = p)
3. Я не могу прямо сейчас, но тем временем прочитаю это: javascript.crockford.com/prototypal.html
4. Не уверен, что вы подразумеваете под «создать свой собственный
Storage
в качестве прототипа». Если бы все, что вы сделали, это удалилиCollection
слой, то каждый тип (если он создает экземпляр своего прототипа какnew Storage()
) по-прежнему страдал бы от проблемы с общей памятью, и несколько экземпляров одного типа не были бы независимыми.
Ответ №3:
Вот очень быстрое решение — измените:
function Collection(){
// assuming you're doing some initialization here, referencing "this"
}
Collection.prototype = new Storage();
Для
function Collection(){
var that = new Storage();
// initialize whatever's necessary, using "that"
return that;
}
или, если вы не выполняете никакой инициализации, просто:
function Collection(){
return new Storage();
}
Это превращается Collection
в Storage
фабрику, которая предотвращает повторное использование одних и тех же Storage
объектов в потомках.