6

我有以下代码定义了一个Car. 每个Car都有颜色和setColor(color)功能。我想添加在被调用时setColor(color)调用的侦听器函数,并且我希望能够随时添加这些侦听器函数。这是一个合适的方法吗?有没有更清洁的方法?

function Car() {

    this._color = 'red';
    this._callbacks = {};

    this.setColor = function(color) {
        this._color = color;
        console.log(">>> set car color to " + color);
        if (this._callbacks['setColor']) {
            this._callbacks['setColor']();
        }
    };

    this.addListener = function(functionName, handler) {
        if (this._callbacks[functionName]) {
            var oldCallback = this._callbacks[functionName];
            this._callbacks[functionName] = function() {
                oldCallback();
                handler();
            }
        } else {
            this._callbacks[functionName] = function() {
                handler();
            }
        }
    };


}

var car = new Car();
car.setColor('blue');
car.addListener('setColor', function() { console.log("This is listener # 1"); });
car.setColor('green');
car.addListener('setColor', function() { console.log("This is listener # 2"); });
car.setColor('orange');

输出:

>>> setColor to blue
>>> setColor to green
This is listener # 1
>>> setColor to orange
This is listener # 1
This is listener # 2
4

3 回答 3

3

我认为存储侦听器的数组将是一种更清洁的方法。此外,您应该使用原型对象,或将半私有属性设为真正的私有变量。

function Car() {
    this._color = 'red';
    this._callbacks = {setColor:[]};
};
Car.prototype.setColor = function(color) {
    this._color = color;
    console.log(">>> set car color to " + color);
    for (var i=0; i<this._callbacks['setColor'].length; i++)
        this._callbacks['setColor'][i]();
};
Car.prototype.addListener = function(functionName, handler) {
    this._callbacks[functionName].push(handler);
};

或者:

function Car() {
    var color = 'red';
    var callbacks = {};

    this.setColor = function(c) {
        color = c;
        console.log(">>> set car color to " + color);
        for (var i=0; 'setColor' in callbacks && i<callbacks['setColor'].length; i++)
            callbacks['setColor'][i]();
    };
    this.addListener = function(functionName, handler) {
        if (functionName in callbacks)
            callbacks[functionName].push(handler);
        else
            callbacks[functionName] = [handler];
    };
}
于 2012-08-21T17:11:33.027 回答
2

可能是这样的。

//the 'class'
function Car() {

    //set up a static listeners object - an array for each method
    Car.listeners = {setColor: []};

    //called by methods on invocation, to fire their listeners
    Car.executeListeners = function(methodName) {
        for (var i=0, len = Car.listeners[methodName].length; i<len; i++)
            Car.listeners[methodName][i].apply(this);
    };

    //method - on invocation, fire any listeners
    this.setColor = function(color) {
        this.color = color;
        Car.executeListeners.call(this, 'setColor');
    };
}

//instance
var car = new Car();

//add a listener to instance.setColor invocations
Car.listeners.setColor.push(function() {
    alert("hello - this car's color is "+this.color);
});

//set color (triggers listener)
car.setColor('orange');

请注意,您将prototype-esq 方法分配给实例而不是原型本身——继承的、可重用功能的地方。继承在性能方面也更快。

于 2012-08-21T17:11:29.533 回答
1

如果它对您有用,那么我认为没有理由不使用它。关于这种方法的一些想法:

  • 您不能设置处理程序的上下文,如果应该针对某个对象调用处理程序,则应该让该addListener方法采用上下文对象并执行callback.call(context). 如果您不关心这一点,则无需担心。
  • 如果早期的回调发生故障,后面的回调将不会被调用。不确定你是否关心这个,但如果你关心的话,你可以改为使用一个数组来存储所有回调,并遍历数组依次调用每个回调。您可能需要将对回调的调用包装在 try/catch 块中,以确保不断调用回调。
  • 您想将当前颜色传递给回调吗?甚至是 Car 对象本身,例如callback(this, this._color)?
  • 您的方法使用了相当数量的闭包。如果您发现性能成为一个问题,那么摆脱闭包会有所帮助。

要考虑的另一件事是使用Object.defineProperty,但这是一个更具风格的选择。

于 2012-08-21T17:08:38.513 回答