Kuroki Kaze
Kuroki Kaze

Reputation: 8481

JS Prototypal Inheritance: childs use the same parent properties?

Let's say I have Player object:

var player = function(name) {
    this.handlers = {};
}

player.prototype.on = function(event, callback) {
    if (!this.handlers[event]) {
        this.handlers[event] = [];
    }
    this.handlers[event].push(callback);
}

It works great, I can create players and each will have its own set of handlers. Now suppose I need to inherit from player:

var testPlayer = function(name) {
    this.name = name;
};

testPlayer.prototype = new player();

Now when I create testPlayer's, each of them share the same handlers property:

var adam = new testPlayer('Adam');
adam.on('test', function(){});

var eve = new testPlayer('Eve');
// eve.handlers == {'test':<function>}

What am I missing here? I understand than every testPlayer's prototype is the same new player object I create when describing child class. But is there some way for all testPlayers to have their own set of handlers?

Upvotes: 5

Views: 197

Answers (2)

bfavaretto
bfavaretto

Reputation: 71939

That sure looks strange for those used to classical inheritance, but it's how prototypal inheritance works. To have a separate handlers object per instance, you need to specify one on the child constructor. That will shadow the prototype's property with the same name:

var testPlayer = function(name) {
    this.name = name;
    this.handlers = {};
};

testPlayer.prototype = new player();

Another solution would be to create this shadowing property on-demand, from your on method:

player.prototype.on = function(event, callback) {
    // Check for a handlers property on the instance
    if(!this.hasOwnProperty('handlers') {
        this.handlers = {};
    }
    if (!this.handlers[event]) {
        this.handlers[event] = [];
    }
    this.handlers[event].push(callback);
}

Interesting fact

This is only a problem if you're modifying properties of an object (or array) on the prototype. If you try to assign to properties that live on the prototype, a local shadowing property will be created automatically (you can't assign to prototype properties from instances).

Upvotes: 5

MaxArt
MaxArt

Reputation: 22637

The problem here is that handlers is a property that was added in the constructor, so when you do

testPlayer.prototype = new player();

you're adding every property of a brand new player object to testPlayer.prototype, and that includes handlers.

So, there's a handlers property in every testPlayer object, and when you add a property to handlers you're adding the property to the object in the prototype, and not of the testPlayer object.

In short, when calling the on method you're adding a property to testPlayer.prototype.handlers, not adam.handlers or eve.handlers.

To be safe, define:

var testPlayer = function(name) {
    this.name = name;
    this.handlers = {};
};

Upvotes: 1

Related Questions