DevJimbo
DevJimbo

Reputation: 108

Javascript class system using prototypes

I'm trying to create a 'class like' inheritance system in JS using functions and prototypes. The idea is to achieve something similar to ExtJS class system without the overhead of the entire framework.

So by now I created an ObjectBase function, modified its prototype to contain some shared functionalities and wrote an injectProto() function to merge two functions prototypes (in a loop).

Here's the injectProto() code

/**
 * Injects a function's prototype into another function's prototype.
 * 
 * @param  {Function} fn1   The function to inject into
 * @param  {Function} fn2   The function to inject from
 * @return {Function}       The injected function
 */
function injectProto(fn1, fn2) {
    for (var m in fn2.prototype) {

        Object.defineProperty(fn1.prototype, m, {
            value: fn2.prototype[m]
        });
    }

    fn1.prototype.constructor = fn1;
    fn1.prototype.parent = fn2.prototype;

    return fn1;
}

And the ObjectBase code

/**
 * Base Object, provides shared functionalities.
 * 
 * [!] All functions extend this one
 * 
 */
var ObjectBase = function(){
};
var ObjectBase.prototype = {
    constructor: ObjectBase,
    parent: none,
    listeners: [],

    addListener: function(listener) {
        this.listeners.push(listener);
    },
    getListeners: function() {
        return this.listeners;
    },
    removeListener: function(listener) {
        for (var i = 0; i < this.listeners.length; i++) {
            if (listener === this.listeners[i]) {
                this.listeners.splice(i, 1);
                break;
            }
        }
    }
};

Now, the problem.

If I create a 'subclass' using injectProto and make new instances of it, they all share the same reference to the listeners property.

So if I add a listener to a subclass instance (foo) every other instance (bar) gets a new listener.

// 1. Inject ObjectBase proto into an empty function
var Foo = injectProto(function(){}, ObjectBase);

// 2. Make some instances
var foo = new Foo();
var bar = new Foo();

// 3. Add a dummy listener to foo
foo.addListener( function(){ console.log("I'm Batman!") } );

// 4. Appreciate my confusion
console.log( foo.getListeners() );
console.log( bar.getListeners() );

Output:

[function()]
[function()]  // Should be empty (?)

Why is that? I did assign every single property from one prototype to the other inside injectProto. Could anyone help me here?

Thanks

Upvotes: 1

Views: 74

Answers (1)

Evan Davis
Evan Davis

Reputation: 36592

To answer your questions, you have the property listeners set on the prototype, so it is shared by all instances. Your constructor needs to create this property on each instance object instead.

function ObjectBase() {
  this.listeners = [];
  this.parent = 'none'; // you have `parent: none` which will error as `none` is undefined
} 

As an aside, don't use delete to remove elements from an array; you'll muck up the indexes. Use a proper array method like splice. Also, don't use for..in on arrays.

Upvotes: 1

Related Questions