Edited at

[JS]KnockoutJS 3.0 Upgrade Noteを読む 第1弾

More than 5 years have passed since last update.


はじめに

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が呼ばれずに、前回評価した値を使うので注意が必要です。


2.3.0.dependentObservable.js

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文が追加されているのが分かります。

見ての通り、値に変化がなかったら更新も通知もしないというものです。


3.0.0.dependentObservable.js

function evaluateImmediate() {

//...
// 変更なければ、更新も通知もしない
if (!dependentObservable['equalityComparer'] || !dependentObservable['equalityComparer'](_latestValue, newValue)) {
dependentObservable["notifySubscribers"](_latestValue, "beforeChange");
_latestValue = newValue;
dependentObservable["notifySubscribers"](_latestValue);
}
//...
}


動かして違いを見る

ソースコード上の違いが分かったので、実際に動きの違いを見てみましょう。

以下のコードには、nというobservableevenNというcomputedがでてきます。nには数値が入り、evenNnが偶数だったらtrueを返すようにしてあります。

2.xの場合、evenNの結果が同じでも通知がいきます。例えば、以下のコードでは、はじめn10が入っており、その後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' });のように、必ず通知がいくようにしましょう。

http://jsfiddle.net/uedatakuya/sKHgg/

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);