使用原型的Javascript类系统

时间:2015-09-15 17:34:26

标签: javascript inheritance prototype

我正在尝试使用函数和原型在JS中创建一个'类似'的继承系统。 我们的想法是在没有整个框架的开销的情况下实现类似于ExtJS类系统的东西。

所以到现在为止我创建了一个ObjectBase函数,修改了它的原型以包含一些共享功能 并编写了一个injectProto()函数来合并两个函数原型(在一个循环中)。

这是injectProto()代码

/**
 * 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;
}

ObjectBase代码

/**
 * 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;
            }
        }
    }
};

现在,问题。

如果我使用injectProto创建'子类'并创建它的新实例,它们都共享相同的引用 listeners财产。

因此,如果我向子类实例(foo)添加一个侦听器,则每个其他实例(bar)都会获得一个新的侦听器。

// 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() );

输出:

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

为什么?我确实将每个属性从一个原型分配到injectProto内的另一个属性。有人可以帮我吗?

由于

1 个答案:

答案 0 :(得分:1)

要回答您的问题,您在原型上设置了属性listeners ,因此所有实例都会共享该属性。您的构造函数需要在每个实例对象上创建此属性。

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

顺便说一句,不要使用delete从数组中删除元素;你会搞砸索引。使用适当的数组方法,如splice。另外,不要在数组上使用for..in