Vue.js や React の様な値の変化を検知して何かしらを発火させる処理を書きたい時、JavaScript では setter, getter を使って次の様に書くことができます。
ゲッター – JavaScript | MDN
セッター – JavaScript | MDN
/**
* 値を監視する機能を持った値を表現するオブジェクト
*/
class ValueWithEventListener<T> {
private _oldVal: T;
private _curVal: T;
private readonly _event: (newVal: T) => void;
/**
* 初期化。
*/
constructor(initVal: T, event: (newVal: T) => void) {
this._oldVal = initVal;
this._curVal = initVal;
this._event = event;
}
/**
* セッター。以前と異なる値がセットされた時にイベントを発生させる
* @param {T} newVal
*/
set val(newVal) {
this._oldVal = this._curVal;
this._curVal = newVal;
if (this._oldVal !== this._curVal) {
// ここに変化を検知した時のイベントを追加
this._event(newVal);
}
}
/**
* ゲッター。外部からは自然に読み取れるようにする
*/
get val() {
return this._curVal;
}
}
// 使用例
const value = new ValueWithEventListener(12, (v) => console.log(v));
value.val = 1; // コンソール上に 1 が表示
value.val = 5; // コンソール上に 5 が表示
value.val = 5; // コンソール上には何も表示されない
value.val = 17; // コンソール上に 17 が表示
console.log(value.val);// コンソール上に 17 が表示
上記コードを元にしたコードを JavaScript で動かしたデモが次です。
getter と setter を使うことで外部からはプロパティを扱っている様に見えてその実、メソッドを呼び出させている、という処理が書けます。これを使うことで値の変化にあわせて色々できます。よくあるのはバリデーションです。例ではコンストラクタでイベント一つ定義するのみでしたが、内部定義のみで外部から渡さない、後付けでいくらでも追加削除可能など色々できます。
値一つにこのような仕掛けが必要になる時は珍しいですし、あった方が良いときもそれほどありませんが、プロパティを見せる代わりに値をセットするメソッドに色々仕込む、というパターンは何かと便利です。