このコードは を6
6 回ログに記録します:
(function timer() {
for (var i=0; i<=5; i++) {
setTimeout(function clog() {console.log(i)}, i*1000);
}
})();
しかし、このコードは...
(function timer() {
for (let i=0; i<=5; i++) {
setTimeout(function clog() {console.log(i)}, i*1000);
}
})();
...次の結果がログに記録されます。
0
1
2
3
4
5
なぜ?
let
これは、各項目を内部スコープに別々にバインドし、var
の最新の値を保持するためでしょうかi
。
ベストアンサー1
とvar
関数スコープがあり、ループの繰り返しすべてに1つの共有バインディングのみがあります。つまり、i
すべてのsetTimeoutコールバックでは、同じ変数はついにループの繰り返しが終了すると 6 になります。
とlet
ブロックスコープがあり、for
ループ内で使用すると、各反復ごとに新しいバインディングが取得されます。つまり、i
すべてのsetTimeoutコールバックでは、違った変数はそれぞれ異なる値を持ちます。最初の変数は 0、次の変数は 1 などです。
したがって、この:
(function timer() {
for (let i = 0; i <= 5; i++) {
setTimeout(function clog() { console.log(i); }, i * 1000);
}
})();
これはvarのみを使用した場合と同等です。
(function timer() {
for (var j = 0; j <= 5; j++) {
(function () {
var i = j;
setTimeout(function clog() { console.log(i); }, i * 1000);
}());
}
})();
即時に呼び出される関数式を使用して、 の例でブロック スコープが機能するのと同様の方法で関数スコープを使用しますlet
。
名前を使わずに短く書くこともできますj
が、それほど明確ではないかもしれません。
(function timer() {
for (var i = 0; i <= 5; i++) {
(function (i) {
setTimeout(function clog() { console.log(i); }, i * 1000);
}(i));
}
})();
矢印関数を使用するとさらに短くなります。
(() => {
for (var i = 0; i <= 5; i++) {
(i => setTimeout(() => console.log(i), i * 1000))(i);
}
})();
(ただし、矢印関数を使用できる場合は、 を使用する理由はありませんvar
。)
これは、 Babel.js が、 が利用できないlet
環境で実行するために例を変換する方法です。let
"use strict";
(function timer() {
var _loop = function (i) {
setTimeout(function clog() {
console.log(i);
}, i * 1000);
};
for (var i = 0; i <= 5; i++) {
_loop(i);
}
})();
感謝マイケル・ギアリーコメントに Babel.js へのリンクを投稿していただきありがとうございます。コメント内のリンクからライブ デモをご覧ください。コードを変更すると、すぐに翻訳が行われます。他の ES6 機能がどのように翻訳されるかを見るのも興味深いです。