Проблема наследования в javascript

#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 объектов в потомках.