オブジェクトプロパティのアクセスをフックする。
Getter,Setterを利用すると、「<オブジェクト>.<プロパティ>」でプロパティにアクセスした際に、指定した関数が実行されるようになります。注意:ただし、IEでは動作しません。
function Kitten( name ) { this._name = name; } // Getter, Setterの設定 // プロパティ名と関数を指定する。 Kitten.prototype.__defineSetter__( "name", function(name) { document.getElementById("stdout").innerHTML += "set name<br/>"; this._name = name; // this.name = name; とすると無限ループにはまるので注意 } ); Kitten.prototype.__defineGetter__( "name", function() { document.getElementById("stdout").innerHTML += "get name<br/>"; return this._name; } ); // モデルを作成。 var mii = new Kitten( "mii" ); // プロパティにアクセス // __defineSetter__, __defineGetter__ で定義した「関数」が実行される。 mii.name = "mii"; var name = mii.name;
リスナ追加ユーティリティで使ってみる。
これを使えば、Modelの更新をリスンするで作ったリスナ追加ユーティリティを使う場合の制約、「モデル更新時は定義されたset()メソッドを使って更新する」必要がなくなります。モデルの更新を普通に"="で行えるようになるわけです。
/** * 更新リスナを追加する。 * @param {Object} target リスナを追加するモデル * @param {String} key イベントキー * @param {Function} listener リスナ関数 */ function addListener( target, key, listener ) { if ( !target.__listeners ) { // ターゲットを拡張する。 target.__listeners = {}; // リスナの記録先を確保。 // setterを上書き。変更を受けてリスナをキックする関数にする。 target["_" + key] = target[key]; target.__defineSetter__(key, function(x) { this["_" + key] = x; if ( this.__listeners[key] ) { for (var i=0; i < this.__listeners[key].length; i++ ) { this.__listeners[key][i]( x ); } } }); } // リスナをターゲットの属性として追加。 if ( !target.__listeners[key] ) { target.__listeners[key] = [] } target.__listeners[key].push( listener ); } // モデル function Kitten( name, age ) { this.name = name; this.age = age; this.state = "stop"; } // モデルを作成。 var mii = new Kitten( "mii", 1 ); // リスナを設定 addListener( mii, "state", function( state ) { document.getElementById("stdout1").innerHTML = state; } ); addListener( mii, "state", function( state ) { document.getElementById("stdout2").innerHTML = state; } ); addListener( mii, "state", function( state ) { document.getElementById("stdout3").innerHTML = state; } ); /** コントローラー */ function KittenService( kitten ) { this.kitten = kitten; } KittenService.prototype = { run: function() { this.kitten.state = "running"; // 普通にフィールドを更新 }, stop: function() { this.kitten.state = "stop"; }, jump: function() { this.kitten.state = "jump!"; } } var service = new KittenService( mii ); service.run();