13
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

var と let

Last updated at Posted at 2016-05-12

var を使った場合

test.js
'use strict'

for (var i = 0; i < 10; ++i) {
  setTimeout(function(argument) {
    console.log(i)
  }, i * 1000);
}
出力
> node test
10
10
10
10
10
10
10
10
10
10

let を使った場合

test.js
'use strict'

for (let i = 0; i < 10; ++i) {
  setTimeout(function(argument) {
    console.log(i)
  }, i * 1000);
}
出力
> node test
0
1
2
3
4
5
6
7
8
9

なぜこうなるのか?

varは、その宣言を行った関数のブロックが有効範囲になります。関数のブロック内でない場所で宣言した場合は、グローバル変数になります(実行環境によってはグローバル変数ではなくモジュール内のローカル変数になったりする場合もありますが)。

'use strict'

for (var i = 0; i < 10; ++i) {

}

var num = 123;
console.log(i); // -> 10

これは、下記のように宣言するのと同じになります。

'use strict'
var i;
var num;

for (i = 0; i < 10; ++i) {

}

num = 123;
console.log(i); // -> 10

つまり、冒頭の例

'use strict'

for (var i = 0; i < 10; ++i) {
  setTimeout(function(argument) {
    console.log(i)
  }, i * 1000);
}

は、

'use strict'
var i;

for (i = 0; i < 10; ++i) {
  setTimeout(function(argument) {
    console.log(i)
  }, i * 1000);
}

というのと同じということになります。iはループが終了した時点で10になるので、setTimeoutの中のconsole.log(i)10を出力します。
これを0,1,2,3,...という出力にしたい場合は、以下のように関数で囲って、iを引数として扱うようにします。

'use strict'

for (var i = 0; i < 10; ++i) {
  (function(i) { // *1
    setTimeout(function(argument) {
      console.log(i)
    }, i * 1000);
  })(i);
}

上記ソース中の*1とコメントを付けた箇所のiは、ループの中で宣言された関数の引数であり、その上の行のfor(var iの箇所にある変数iとは別物です。
わかりやすくするために引数名を書き換えるとこのようになります。

'use strict'

for (var i = 0; i < 10; ++i) {
  (function(arg) {
    setTimeout(function(argument) {
      console.log(arg)
    }, i * 1000);
  })(i);
}

引数を使わずに別の変数に代入するという方法もあります。

'use strict'

for (var idx = 0; idx < 10; idx++) {
  (function() {
    var i = idx; // こうすると、iはこの関数ブロックのローカル変数になる
    setTimeout(function() {
      console.log(i);
    }, i * 1000);
  })()
}

let

varとは違い、letは宣言された箇所を含む{ ... }で囲まれた範囲のブロックが有効範囲になります。

'use strict'
let a;

for (let i = 0; i < 10; ++i) {
  // a が使える
  // i が使える
}

// a が使える
// i は使えない!

厳密には異なるのかもしれませんが、下記のコードは

'use strict'

for (let i = 0; i < 10; i++) {
  setTimeout(function() {
    console.log(i);
  }, i * 1000);
}

このように書くのと同じです。

'use strict'

for (var idx = 0; idx < 10; idx++) {
  (function() {
    var i = idx;
    setTimeout(function() {
      console.log(i);
    }, i * 1000);
  })()
}

letの方が理に適った動作だと思います。Node.jsなどES6の機能が使えるJavaScript実行環境では(違いがあることを把握したうえで)積極的にletの方を使って行くのが良いでしょう。

13
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?