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

October 27, 2013

この記事はQiitaの記事をエクスポートしたものです。内容が古くなっている可能性があります。

はじめに

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という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);