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)ですか?
  • あなたのアプローチはかなりの量のクロージャを使用しています。パフォーマンスが問題になっていることに気付いた場合は、クロージャを取り除くことがそれを助けます。

考慮すべきもう1つのことは、を使用することですObject.definePropertyが、それはより文体的な選択です。

于 2012-08-21T17:08:38.513 に答える