0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

for 文の let 宣言のスコープの謎 ~どうもシンプルに while に直すことができないらしい~

Posted at

はじめに

for 文は while に直すことができると聞きますが 例えば次の様な コードがあったとします。

for (let i = 0, imax = 5; i < imax; i++) {
  console.log('c', i);
  setTimeout(() => console.log('t', i));
}

結果は次の様になります

c 0
c 1
c 2
c 3
c 4
t 0
t 1
t 2
t 3
t 4

うまくいかない while への直し方

while に直してみましょう

こうでしょうか?

{
  let i = 0, imax = 5;
  while (i < imax) {
    console.log('c', i);
    setTimeout(() => console.log('t', i));
    i++;
  }
}

c 0
c 1
c 2
c 3
c 4
t 5
t 5
t 5
t 5
t 5

どうも i の 宣言を while の外にしてしまうと スコープが広すぎて setTimeout に渡している変数の値が動的に変わって最終値になってしまうみたいです。

うまくいった while への直し方

つまりこれと同じ

{
  let i_ = 0, imax = 5;
  while (i_ < imax) {
    let i = i_;
    console.log('c', i);
    setTimeout(() => console.log('t', i));
    i_++;
  }
}

c 0
c 1
c 2
c 3
c 4
t 0
t 1
t 2
t 3
t 4

よしうまくいきました。

おわりに

つまり for は while にシンプルに直すことはできない でした。

実態のスコープ的には for 文中の let 宣言しても何かシンプルじゃないスコープ(ブロック一枚噛ませてる?)があるのでは?といった感じがありました。

以上。

余談

もしも for 文で var で宣言すると次の様になります。

for (var i = 0, imax = 5; i < imax; i++) {
  console.log('c', i);
  setTimeout(() => console.log('t', i));
}

c 0
c 1
c 2
c 3
c 4
t 5
t 5
t 5
t 5
t 5

実行結果は予想の通りです。i の宣言 が function の先頭になる(つまりfunction scopeな)ので for の外で宣言したのと同等となり、こうなります。かわいいですね。

余談 その 2

for 文の説明自体は 14.7.4.2 Runtime Semantics: ForLoopEvaluation にあるのですが、

var 宣言じゃない方の 手動 for 文 ForStatement : for ( Expressionopt ; Expressionopt ; Expressionopt ) Statement
var 宣言の方の 手動 for 文 ForStatement : for ( var VariableDeclarationList ; Expressionopt ; Expressionopt ) Statement と明示的にわけているところをみるとスコープ回りの挙動をそこで制御しているのかなといった感じはします。

ForStatement : for ( Expressionopt ; Expressionopt ; Expressionopt ) Statement

  1. If the first Expression is present, then
    a. Let exprRef be ? Evaluation of the first Expression.
    b. Perform ? GetValue(exprRef).
  2. If the second Expression is present, let test be the second Expression; otherwise, let test be empty.
  3. If the third Expression is present, let increment be the third Expression; otherwise, let increment be empty.
  4. Return ? ForBodyEvaluation(test, increment, Statement, « », labelSet).

ForStatement : for ( var VariableDeclarationList ; Expressionopt ; Expressionopt ) Statement

  1. Perform ? Evaluation of VariableDeclarationList.
  2. If the first Expression is present, let test be the first Expression; otherwise, let test be empty.
  3. If the second Expression is present, let increment be the second Expression; otherwise, let increment be empty.
  4. Return ? ForBodyEvaluation(test, increment, Statement, « », labelSet).

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?