5

I have the following code:

function Rune(){
    this.subSpells = [];
}
function Modifier(){
    this.type = "modifier";
}
Modifier.prototype = new Rune();

function RuneFactory(effect, inheritsFrom, initialValue){
    var toReturn = function(){}; 
    toReturn.prototype = new inheritsFrom();
    toReturn.prototype.subSpells[effect] = initialValue;
    return toReturn;
}

Duration = RuneFactory("duration", Modifier, 1);
Quicken = RuneFactory("quicken", Modifier, 1);

x = new Duration();
y = new Quicken();

Both x.subSpells.duration and x.subSpells.quicken equal 1. Same with y. I want x.subSpells.quicken and y.subSpells.duration to be undefined.

If I do the following for the Duration and Quicken definitions, I get the behaviour I want.

Duration = RuneFactory("duration", Rune, 1);
Quicken = RuneFactory("quicken", Rune, 1);

I think there is a problem with double inheritance. Can anyone tell me how to change my RuneFactory code such that it works with double inheritance and/or explain what's breaking? If at all possible, I would like to avoid using a framework.

Thank you!

ebdavis
  • 105
  • 8
  • 4
    Add following line in the Modifier function `Rune.apply(this, arguments);`, since `subSpells` defined at prototype level, all instance of Rune refer to same object. – Givi Jan 02 '14 at 10:02
  • 2
    +1 However, be aware that the `Rune` constructor will be called twice, once to populate the prototype of `Modifier`, and once for the instanciation. You should replace `Modifier.prototype = new Rune()` with `Modifier.prototype = Object.create(Rune.prototype)` to avoid this. Note : `Object.create` is a recent feature, go here for a workaround in old browsers if needed : http://stackoverflow.com/a/20879584/1636522. –  Jan 02 '14 at 10:07
  • 1
    Side note: you are specifying a string index to an array--shouldn't subspells be an object instead? – ErikE Jan 02 '14 at 10:10
  • I cannot reproduce your problem. In either way, I get a value of `1`. And you are explicitly setting it to `1` with the `initialValue`. So why do you expect it to be `undefined`. Or am I missing something? – basilikum Jan 02 '14 at 10:12
  • The inheritance looks fine to me - whether you pass in Rune or Modifier to inherit from, the result still has a subSpells property. – johnnycardy Jan 02 '14 at 10:16
  • possible duplicate of [Crockford's Prototypal inheritance - Issues with nested objects](http://stackoverflow.com/questions/10131052/crockfords-prototypal-inheritance-issues-with-nested-objects) – Bergi Jan 02 '14 at 10:57

2 Answers2

3

Add following line in the Modifier function Rune.apply(this, arguments);, since subSpells defined at prototype level, all instance of Rune refer to same object.
And, change type of subSpells from array to object.

As mentioned by wared would be better if you use Parasitic Combination Inheritance Pattern.

"I think there is a problem with double inheritance" - No!

At first, there's no such thing as double inheritance, you have same problem as with Modifier constructor, your instances refer to same subSpells object (Array in your case. read about Primitive value vs Reference value or References and Values it's about .NET, but basic principles are the same in programming).

Look at How does JavaScript .prototype work?


Look at jsFiddle

function inheritPrototype(subType, superType) {
    var prototype = Object.create(superType.prototype, {
        constructor: {
            value: subType,
            enumerable: true
        }
    });

    subType.prototype = prototype;
}

function Rune() {
    this.subSpells = {};
}

function Modifier() {
    Rune.apply(this, arguments);
    this.type = "modifier";
}

inheritPrototype(Modifier, Rune);

function RuneFactory(effect, inheritsFrom, initialValue) {
    function toReturn() {
        inheritsFrom.apply(this, arguments); // you should bind this
        this.subSpells[effect] = initialValue;
    }
    inheritPrototype(toReturn, inheritsFrom);
    return toReturn;
}

Duration = RuneFactory("duration", Modifier, 1);
Quicken = RuneFactory("quicken", Modifier, 1);

x = new Duration();
y = new Quicken();

console.log(x.subSpells.duration); // 1
console.log(x.subSpells.quicken); // undefined

console.log(y.subSpells.duration); // undefined
console.log(y.subSpells.quicken); // 1
Community
  • 1
  • 1
Givi
  • 1,674
  • 2
  • 20
  • 35
1

The correct way to inherit in JavaScript is:

function Parent(arg1, arg2) {
    // Do something
}

function Child(arg1, arg2) {
    Parent.call(this, arg1, arg2); // or Parent.apply(this, arguments)
}

Child.prototype = Object.create(Parent.prototype, {
    constructor: {
        value: Child
    }
});

So you will need something like this:

function Rune(){
    this.subSpells = {};
}

function Modifier(){
    Rune.apply(this, arguments);
    this.type = "modifier";
}

Modifier.prototype = Object.create(Rune.prototype, {
    constructor: {
        value: Modifier
    }
});

function RuneFactory(effect, inheritsFrom, initialValue){
    var toReturn = function(){}; 
    toReturn.prototype = Object.create(inheritsFrom.prototype, {
        constructor: {
            value: Modifier
        }
    });
    toReturn.prototype.subSpells = {};
    toReturn.prototype.subSpells[effect] = initialValue;
    return toReturn;
}

Duration = RuneFactory("duration", Modifier, 1);
Quicken = RuneFactory("quicken", Modifier, 1);

x = new Duration();
y = new Quicken();

NOTE: in your question .subSpells is an array but you assign an member as it would be an object!

micnic
  • 10,915
  • 5
  • 44
  • 55