setTimeout 関数を使用すると let バインディングと var バインディングの動作が異なるのはなぜですか? [重複] 質問する

setTimeout 関数を使用すると let バインディングと var バインディングの動作が異なるのはなぜですか? [重複] 質問する

このコードは を66 回ログに記録します:

(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 機能がどのように翻訳されるかを見るのも興味深いです。

おすすめ記事