はじめに
KnockoutJSの3.0がリリースされましたね。
Upgrade Notesを読んで分かった事をまとめます。
今回は、「1. Computed properties now notify only when their value changes」を読みます。
説明を読むと、2.xまではcomputed
が評価された際は、必ず変更したとういう通知が行くそうです。subscribe
でコールバックを仕掛ければ、その通知を受取ることができます。3.0では、computed
が評価された結果が前回と同じであれば、通知がいかないようになっています。
文章だけ見ると、分かったようで分からないので、KnockoutJSのソースコードを読みましょう。
2.3.0のコード
2.3.0のコードを見てみます。
https://github.com/knockout/knockout/blob/v2.3.0/src/subscribables/dependentObservable.js#L80-L84
evaluateImmediate
が呼ばれると、値の更新とその事の通知が行なわれているのが分かります。ただし、依存しているobservableが更新されないとevaluateImmediate
が呼ばれずに、前回評価した値を使うので注意が必要です。
function evaluateImmediate() {
//...
// ここで値の更新と通知している
dependentObservable["notifySubscribers"](_latestValue, "beforeChange");
_latestValue = newValue;
dependentObservable["notifySubscribers"](_latestValue);
//...
}
3.0.0のコード
次に、3.0.0のコードを見てみます。
https://github.com/knockout/knockout/blob/v3.0.0/src/subscribables/dependentObservable.js#L85-L91
2.3.0に無かったif文が追加されているのが分かります。
見ての通り、値に変化がなかったら更新も通知もしないというものです。
function evaluateImmediate() {
//...
// 変更なければ、更新も通知もしない
if (!dependentObservable['equalityComparer'] || !dependentObservable['equalityComparer'](_latestValue, newValue)) {
dependentObservable["notifySubscribers"](_latestValue, "beforeChange");
_latestValue = newValue;
dependentObservable["notifySubscribers"](_latestValue);
}
//...
}
動かして違いを見る
ソースコード上の違いが分かったので、実際に動きの違いを見てみましょう。
以下のコードには、n
というobservable
とevenN
というcomputed
がでてきます。n
には数値が入り、evenN
はn
が偶数だったらtrue
を返すようにしてあります。
2.xの場合、evenN
の結果が同じでも通知がいきます。例えば、以下のコードでは、はじめn
に10
が入っており、その後20
に変わっています。どちらも偶数なので、evenN
は共にtrue
になります。evenN
の値は変わっていないので、通知はいってほしくないですが、2.xの場合は通知がいってしまいます。
一方、3.0.0の場合は、n(20)
ではevenN
の値は変わらないので、通知はいいません。n(21)
では、奇数になるので、evenN
の値はfalse
となるので、通知がいきます。
JSFiddleで動作を確認してみましょう。
2.3.0:http://jsfiddle.net/uedatakuya/TdErN/
3.0.0:http://jsfiddle.net/uedatakuya/xxK9E/
var n = ko.observable(10);
var evenN = ko.computed(function() {
var isEven = n() % 2 == 0;
alert(isEven);
console.log(isEven);
return isEven;
});
evenN.subscribe(function(newValue) {
alert("change evenN to " + newValue);
console.log("change evenN to " + newValue);
});
n(20);
n(21);
挙動が変わると困る場合
上述のように、3.0.0では2.xと挙動が変わります。2.xの挙動と同じようにしたい場合は、Upgrade Notesに書いてある通り、myComputed.extend({ notify: 'always' });
のように、必ず通知がいくようにしましょう。
var n = ko.observable(10);
var evenN = ko.computed(function() {
var isEven = n() % 2 == 0;
alert(isEven);
console.log(isEven);
return isEven;
});
// 必ず通知がいくようにする
evenN.extend({notify: 'always'});
evenN.subscribe(function(newValue) {
alert("change evenN to " + newValue);
console.log("change evenN to " + newValue);
});
n(20);
n(21);