忍者ブログ

Memeplexes

プログラミング、3DCGとその他いろいろについて

letキーワードでクロージャからforのインデックスをキャプチャ

最近letキーワードを使うとTypeScriptでクロージャのforのインデックス変数キャプチャを簡単にできることを知りました!実はそこそこ昔からあったようですが忘れないためにもメモします。


変数のキャプチャをしないと

TypeScriptでは(TypeScriptに限った話ではありませんが)、ループのインデックスをクロージャの中でそのまま使うと変な値になってしまいます。

コード

var getNumbers: (() => number)[] = [];

for (var i = 0; i < 3; i++){
	getNumbers.push(() => i);
}

for (var getNumber of getNumbers) {
	document.writeln(getNumber() + "<br>");
}

結果

3
3
3

変です!0 1 2と表示しようとして「0から2までの数字をそれぞれ返す関数3つ」の結果を表示したはずなのに、そのどれでもない3が表示されています!しかも3回も!!!

これは、3つの関数が実は同じ1つの変数を返すからで、ループのインデックスiが最後に3になったために、3つの関数すべてが3を返してしまったのです。上書きされてしまったので最後に保存したデータしか残っていないという状態です。

変数の値をコピー

この問題を解決するには、3つの関数が違う別々の変数を返すように書き換えなければいけません。上書きするのではなく、別の変数にデータをとっておくのです。

解決策

var getNumbers: (() => number)[] = [];

for (var i = 0; i < 3; i++){
	(() => {
		var index = i;
		getNumbers.push(() => index);
	})();
}

for (var getNumber of getNumbers) {
	document.writeln(getNumber() + "<br>");
}

結果

0
1
2

うまくいきました!前回は同じ変数を返していたので同じ数字が3連続してしまいましたが、今回は変数を3つの違う変数にコピーしたので、きちんと値が上書きされずに保存されています。

letを使う

ところがこれにはもっとかんたんな解決策があって、それはTypeScript1.5から導入されたletキーワードです。実際にはJavaScript時代からあったそうですが、普及しなかったようです。

解決策その2

var getNumbers: (() => number)[] = [];

for (let i = 0; i < 3; i++){
	getNumbers.push(() => i);
}

for (var getNumber of getNumbers) {
	document.writeln(getNumber() + "<br>");
}

結果

0
1
2

うまくいきました!こっちのほうが上の例よりかんたんなのでこっちを使いたいですね。

letはブロック内でのみ変数が生きているというような意味なので、i=0のときとi=1のときでiが同じ名前の別物になっています。

拍手[0回]

PR