【问题标题】:Adding listener functions to a JavaScript object将侦听器函数添加到 JavaScript 对象
【发布时间】:2012-08-17 01:59:15
【问题描述】:

我有以下代码定义了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

【问题讨论】:

    标签: javascript function callback listener


    【解决方案1】:

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

    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];
        };
    }
    

    【讨论】:

    • 我调整了自己的代码以大致匹配您的第二个选项。与我在上述评论中发布的类似问题:使用原型对象有什么好处?而且我也从来不确定如何准确地“使半私有属性成为真正的私有变量”。你能再解释一下吗?
    • Advantages of using prototype, vs defining methods straight in the constructor?Declaring javascript object method in constructor function vs. in prototype。在第一个示例中,对象的属性是公共的(只有下划线表示它们不应该从外部使用) - 在第二个示例中,我使用了作用域为构造函数的局部变量,它们只能由两个特权访问方法。 (谷歌足够的流行语;-)
    • @Utkanos 的回答也很好,但这里给出的第二个选项是我最终使用的解决方案。这消除了我的变量 colorcallbacks 的半私密性,并将回调函数放在数组而不是闭包中。
    【解决方案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 方法分配给实例而不是原型本身——这是继承、可重用功能的地方。继承在性能方面也更快。

    【讨论】:

    • 我只是对我对原型设计工作原理的了解有点自信。将每个 Car.{method} 函数设为 this.{method} 函数有什么问题? (换句话说,将 Car 函数体内的三个“Car”实例替换为“this”。
    • 为什么要让听众静态化?我认为这不是一个好主意。
    • @Mokt - 原型是可继承、可重用功能的地方。您完成它的方式将起作用,但基于实例的方法稍慢(当然,您必须有很多重要的方法才能注意到差异),这意味着它们是自己的属性(因此它们将是可枚举的) 的实例而不是继承。
    • @Bergi - 为什么会这样?你不细说。似乎工作正常。一,所有方法的监听器的静态位置。适用于多个实例。 jsfiddle.net/akxVD/1 很高兴得到纠正。
    • @Utkanos:是的,它确实有效,但我认为它不是有意的。破坏 OPs 功能至少需要对此发表评论
    【解决方案3】:

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

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

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

    【讨论】:

      猜你喜欢
      • 2018-12-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-28
      • 1970-01-01
      • 2021-04-17
      • 2013-02-17
      • 2011-09-28
      相关资源
      最近更新 更多