关于 JavaScript 中的关键词 'let'

About the key word 'let' in JavaScript

var funcs=[];
for(let i=0;i<3;i++){
 funcs[i]=function(){
  
  return i;
 }
 
}
alert(funcs[1]);
alert(funcs[1]());
window 警报 2 次。 第一个是这样的:

function (){
  return i;
 }

第二个像这样

1

但是我不知道为什么funcs[1]可以执行不报错'i is undefined';

因为在循环中创建的函数 关闭了 创建时处于活动状态的 lexical environment。该词法环境(概念上)是一个对象,其中包含在其中定义的局部变量(以及其他一些东西),包括 i 变量,在本例中是为循环体的特定迭代创建的变量(因为非常特殊的方式 for 在其初始值设定项中处理 let 声明)。这是"closure,"核心技术之一的概念JavaScript。即使执行超出给定词法环境关联的范围(函数 returns,我们继续循环的下一次迭代,等等),如果任何东西仍然引用该环境对象,就像它赖以生存的所有物体一样。

由于 for 在其初始化程序中如何处理 letfuncs 中的每个条目都有自己的词法环境,因此也有自己的 i 副本。

当您调用其中一个函数时,会创建一个新的环境对象,其 "outer" 环境设置为附加到该函数的环境。当您在函数代码中引用 i 时,它首先查看函数的环境,如果在那里找不到 i,它会查看外部环境——找到它的地方(在本例中)并使用它。

在您说过的评论中

If you use 'var' not 'let', it will always return '3'

完全正确。使用 vari 将被提升到与 for 循环所在的函数关联的环境对象(或者全局函数,如果这是全局代码)。所以在循环中创建的所有函数共享相同的 i,当你调用它们时,它的值是 3.

这是let/constvar的本质区别之一:letconst有块作用域,而for 在其初始化程序中对 let 进行了特殊处理。

让我们来看看这些不同的环境对象是如何创建的。假设这段代码:

funtion example() {
    const funcs = [];
    for (let i = 0; i < 3; ++i) {
        funcs[i] = function() {
            return i;
        };
    }
    funcs[1](); // 1
}
example();

当我们调用 example 时,在 const funcs = [] 之后但在 for 循环开始之前,当前环境对象是为调用 example 创建的对象,所以我们在内存中有这样的东西(忽略一些细节):

                                          +−−−−−−−−−−−−−−−−+
    current env>−−−−−−−−−−−−−−−−−−−−−−−−−>| `example` Call |
                                          |  Env Object    |
                                          +−−−−−−−−−−−−−−−−+
                                          | [[Outer]]      |>−−−>(The env obj for when `example` was created.)
                                          | funcs          |>−+
                                          +−−−−−−−−−−−−−−−−+  |  +−−−−−−−−−−−+
                                                              +−>|  (array)  |
                                                                 +−−−−−−−−−−−+
                                                                 | length: 0 |
                                                                 +−−−−−−−−−−−+

现在,for 循环开始:创建一个新的每迭代环境对象作为 "current" 对象,前一个作为其 "outer" 环境。 i 变量在新的每次迭代环境对象中创建,并赋予值 0:

                  +−−−−−−−−−−−−−−+
   current env>−−>| Iteration 0  |
                  | Env Object   |
                  +−−−−−−−−−−−−−−+         +−−−−−−−−−−−−−−−−+
                  | [[Outer]]    |>−−−−−−−>| `example` Call |                                                  
                  | i: 0         |         |  Env Object    |                                                  
                  +−−−−−−−−−−−−−−+         +−−−−−−−−−−−−−−−−+                                                  
                                           | [[Outer]]      |>−−−>(The env obj for when `example` was created.)
                                           | funcs          |>−+
                                           +−−−−−−−−−−−−−−−−+  |  +−−−−−−−−−−−+
                                                               +−>|  (array)  |
                                                                  +−−−−−−−−−−−+
                                                                  | length: 0 |
                                                                  +−−−−−−−−−−−+

在循环迭代期间,我们创建一个函数并将其存储在funcs中。该函数获取对当前环境对象的引用,它保持为 [[Environment]](这是一个实现的东西,如果您查看该函数,您实际上不会发现它在代码级别不可访问,只是在 JavaScript 引擎中):

                   +−−−−−−−−−−−−−−+
    current env>−+>| Iteration 0  |
                /  | Env Object   |         +−−−−−−−−−−−−−−−−+
               /   +−−−−−−−−−−−−−−+         | `example` Call |
              +    | [[Outer]]    |>−−−−−−−>|  Env Object    |
              |    | i: 0         |         +−−−−−−−−−−−−−−−−+
              |    +−−−−−−−−−−−−−−+         | [[Outer]]      |>−−−>(The env obj for when `example` was created.)
              |                             | funcs          |>−+
              |                             +−−−−−−−−−−−−−−−−+  |  +−−−−−−−−−−−+
              |                                                 +−>|  (array)  |
              |                                                    +−−−−−−−−−−−+
              |                                                    | length: 1 |       +−−−−−−−−−−−−−−−−−+
              |                                                    | 0         |>−−−−−>|   Function 0    |
              |                                                    +−−−−−−−−−−−+       +−−−−−−−−−−−−−−−−−+
              |                                                                        | [[Environment]] |>−−−−−+
              |                                                                        +−−−−−−−−−−−−−−−−−+      |
              |                                                                                                 |
              +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+

现在这就是 for 初始化器中对 let 的巧妙处理(事实上,letconstfor 循环体):为 next 迭代创建了一个 new 环境对象,i 的值为从上一次迭代的i复制到下一次迭代的i。所以我们有:

                   +−−−−−−−−−−−−−−+
+−−−−−−−−−−−−−−−−−>| Iteration 0  |
|                  | Env Object   |         +−−−−−−−−−−−−−−−−+
|                  +−−−−−−−−−−−−−−+         | `example` Call |
|                  | [[Outer]]    |>−−−+−−−>|  Env Object    |
|                  | i: 0         |   /     +−−−−−−−−−−−−−−−−+
|                  +−−−−−−−−−−−−−−+  +      | [[Outer]]      |>−−−>(The env obj for when `example` was created.)
|                                    |      | funcs          |>−−−+
|                                    |      +−−−−−−−−−−−−−−−−+    |   +−−−−−−−−−−−+
|                                    |                            +−−>|  (array)  |
|                  +−−−−−−−−−−−−−−+  |                                +−−−−−−−−−−−+
|   current env>−+>| Iteration 1  |  |                                | length: 1 |     +−−−−−−−−−−−−−−−−−+
|                  | Env Object   |  |                                | 0         |>−−−>|   Function 0    |
|                  +−−−−−−−−−−−−−−+  |                                +−−−−−−−−−−−+     +−−−−−−−−−−−−−−−−−+
|                  | [[Outer]]    |>−+                                                  | [[Environment]] |>−+
|                  | i: 0         |                                                     +−−−−−−−−−−−−−−−−−+  |
|                  +−−−−−−−−−−−−−−+                                                                          |
+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+

然后新环境中的 i 增加到 1,并创建一个新函数并存储在 funcs 中,给我们:

                   +−−−−−−−−−−−−−−+
+−−−−−−−−−−−−−−−−−>| Iteration 0  |
|                  | Env Object   |         +−−−−−−−−−−−−−−−−+
|                  +−−−−−−−−−−−−−−+         | `example` Call |
|                  | [[Outer]]    |>−−−+−−−>|  Env Object    |
|                  | i: 0         |   /     +−−−−−−−−−−−−−−−−+
|                  +−−−−−−−−−−−−−−+  +      | [[Outer]]      |>−−−>(The env obj for when `example` was created.)
|                                    |      | funcs          |>−−−+
|                                    |      +−−−−−−−−−−−−−−−−+    |   +−−−−−−−−−−−+
|                                    |                            +−−>|  (array)  |
|                  +−−−−−−−−−−−−−−+  |                                +−−−−−−−−−−−+
|   current env>−+>| Iteration 1  |  |                                | length: 2 |     +−−−−−−−−−−−−−−−−−+
|               /  | Env Object   |  |                                | 0         |>−−−>|   Function 0    |
|              /   +−−−−−−−−−−−−−−+  |                                | 1         |>−+  +−−−−−−−−−−−−−−−−−+
|             +    | [[Outer]]    |>−+                                +−−−−−−−−−−−+  |  | [[Environment]] |>−−−+
|             |    | i: 1         |                                                  |  +−−−−−−−−−−−−−−−−−+    |
|             |    +−−−−−−−−−−−−−−+                                                  |                         |
|             |                                                                      |  +−−−−−−−−−−−−−−−−−+    |
|             |                                                                      +−>|   Function 1    |    |
|             |                                                                         +−−−−−−−−−−−−−−−−−+    |
|             |                                                                         | [[Environment]] |>−+ |
|             |                                                                         +−−−−−−−−−−−−−−−−−+  | |
|             |                                                                                              | |
|             +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+ |
|                                                                                                              |
+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+

然后在该迭代结束时,我们为最后一次迭代再次执行整个操作,并得到:

                   +−−−−−−−−−−−−−−+
+−−−−−−−−−−−−−−−−−>| Iteration 0  |
|                  | Env Object   |         +−−−−−−−−−−−−−−−−+
|                  +−−−−−−−−−−−−−−+         | `example` Call |
|                  | [[Outer]]    |>−−+−−+ −>|  Env Object    |
|                  | i: 0         |  /  /   +−−−−−−−−−−−−−−−−+
|                  +−−−−−−−−−−−−−−+  | |    | [[Outer]]      |>−−−>(The env obj for when `example` was created.)
|                                    | |    | funcs          |>−−−+
|                                    | |    +−−−−−−−−−−−−−−−−+    |   +−−−−−−−−−−−+
|                                    | |                          +−−>|  (array)  |
|                  +−−−−−−−−−−−−−−+  | |                              +−−−−−−−−−−−+
| +−−−−−−−−−−−−−−−>| Iteration 1  |  | |                              | length: 3 |       +−−−−−−−−−−−−−−−−−+
| |                | Env Object   |  | |                              | 0         |>−−−−−>|   Function 0    |
| |                +−−−−−−−−−−−−−−+  | |                              | 1         |>−−−+  +−−−−−−−−−−−−−−−−−+
| |                | [[Outer]]    |>−+ |                              | 2         |>−+ |  | [[Environment]] |>−−−−−+
| |                | i: 1         |    |                              +−−−−−−−−−−−+  | |  +−−−−−−−−−−−−−−−−−+      |
| |                +−−−−−−−−−−−−−−+    |                                             | |                           |
| |                                    |                                             | |  +−−−−−−−−−−−−−−−−−+      |
| |                +−−−−−−−−−−−−−−+    |                                             | +−>|   Function 1    |      |
| | current env>−+>| Iteration 2  |    |                                             |    +−−−−−−−−−−−−−−−−−+      |
| |             /  | Env Object   |    |                                             |    | [[Environment]] |>−−−+ |
| |            +   +−−−−−−−−−−−−−−+    |                                             |    +−−−−−−−−−−−−−−−−−+    | |
| |            |   | [[Outer]]    |>−−−+                                             |                           | |
| |            |   | i: 2         |                                                  |    +−−−−−−−−−−−−−−−−−+    | |
| |            |   +−−−−−−−−−−−−−−+                                                  +−−−>|   Function 2    |    | |
| |            |                                                                          +−−−−−−−−−−−−−−−−−+    | |
| |            |                                                                          | [[Environment]] |>−+ | |
| |            |                                                                          +−−−−−−−−−−−−−−−−−+  | | |
| |            |                                                                                               | | |
| |            +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+ | |
| |                                                                                                              | |
| +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+ |
|                                                                                                                  |
+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+

当我们调用funcs[1]()时,会创建一个调用环境,并将其[[Outer]]环境设置为函数的[[Environment]]。所以就在我们 return i 之前,我们有(省略一些细节):

               +−−−−−−−−−−−−−−+
current env>−−>| Call to      |
               | `funcs[1]()` |
               | Env Object   |
               +−−−−−−−−−−−−−−+
               | [[Outer]]    |>−−+
               +−−−−−−−−−−−−−−+   |
                                  |    
                                  |       +−−−−−−−−−−−−−−−−+
             +−−−−−−−−−−−−−−−−−−−−+       | `example` call |
             |                       +−−−>| Env Object     |
             |                       |    +−−−−−−−−−−−−−−−−+
             |                       |    | [[Outer]]      |>−−−>(The env obj for when `example` was created.)
             |                       |    | funcs          |>−+
             |                       |    +−−−−−−−−−−−−−−−−+  |  +−−−−−−−−−−−+
             |                       |                        +−>|  (array)  |
             \     +−−−−−−−−−−−−−−+  |                           +−−−−−−−−−−−+
  +−−−−−−−−−−−+−−−>| Iteraton 1   |  |                           | length: 3 |       +−−−−−−−−−−−−−−−−−+
  |                | Env Object   |  |                           | 0         |>−−−−−>|   (function)    |
  |                +−−−−−−−−−−−−−−+  |                           | 1         |>−−−+  +−−−−−−−−−−−−−−−−−+
  |                | [[Outer]]    |>−+                           | 2         |>−+ |  | [[Environment]] |>...
  |                | i: 1         |                              +−−−−−−−−−−−+  | |  +−−−−−−−−−−−−−−−−−+
  |                +−−−−−−−−−−−−−−+                                             | |                     
  |                                                                             | |  +−−−−−−−−−−−−−−−−−+
  |                                                                             | +−>|   (function)    |
  |                                                                             |    +−−−−−−−−−−−−−−−−−+
  |                                                                             |    | [[Environment]] |>−−−−+
  |                                                                             |    +−−−−−−−−−−−−−−−−−+     |
  |                                                                             |                            |
  |                                                                             |    +−−−−−−−−−−−−−−−−−+     |
  |                                                                             +−−−>|   (function)    |     |
  |                                                                                  +−−−−−−−−−−−−−−−−−+     |
  |                                                                                  | [[Environment]] |>... |
  |                                                                                  +−−−−−−−−−−−−−−−−−+     |
  |                                                                                                          |
  |                                                                                                          |
  +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+

当函数查找 i 时,它会在当前环境对象中查找。由于它不存在,它会查找 [[Outer]] 对象。它在那里找到它,其值为 1,因此这就是它使用的值。

相反,如果我们使用 var,则 i 被提升到环境对象以调用 example(其中 funcs 是),所以在循环我们有这个代替(注意 i 不再在每次迭代环境中):

                   +−−−−−−−−−−−−−−+
+−−−−−−−−−−−−−−−−−>| Iteration 0  |
|                  | Env Object   |         +−−−−−−−−−−−−−−−−+
|                  +−−−−−−−−−−−−−−+         | `example` Call |
|                  | [[Outer]]    |>−−+−−+−>|  Env Object    |
|                  +−−−−−−−−−−−−−−+  /  /   +−−−−−−−−−−−−−−−−+
|                                    | |    | [[Outer]]      |>−−−>(The env obj for when `example` was created.)
|                                    | |    | i: 3           |
|                                    | |    | funcs          |>−−−+
|                                    | |    +−−−−−−−−−−−−−−−−+    |   +−−−−−−−−−−−+
|                                    | |                          +−−>|  (array)  |
|                  +−−−−−−−−−−−−−−+  | |                              +−−−−−−−−−−−+
| +−−−−−−−−−−−−−−−>| Iteration 1  |  | |                              | length: 3 |       +−−−−−−−−−−−−−−−−−+
| |                | Env Object   |  | |                              | 0         |>−−−−−>|   Function 0    |
| |                +−−−−−−−−−−−−−−+  | |                              | 1         |>−−−+  +−−−−−−−−−−−−−−−−−+
| |                | [[Outer]]    |>−+ |                              | 2         |>−+ |  | [[Environment]] |>−−−−−+
| |                +−−−−−−−−−−−−−−+    |                              +−−−−−−−−−−−+  | |  +−−−−−−−−−−−−−−−−−+      |
| |                                    |                                             | |                           |
| |                                    |                                             | |  +−−−−−−−−−−−−−−−−−+      |
| |                +−−−−−−−−−−−−−−+    |                                             | +−>|   Function 1    |      |
| | current env>−+>| Iteration 2  |    |                                             |    +−−−−−−−−−−−−−−−−−+      |
| |             /  | Env Object   |    |                                             |    | [[Environment]] |>−−−+ |
| |            +   +−−−−−−−−−−−−−−+    |                                             |    +−−−−−−−−−−−−−−−−−+    | |
| |            |   | [[Outer]]    |>−−−+                                             |                           | |
| |            |   +−−−−−−−−−−−−−−+                                                  |    +−−−−−−−−−−−−−−−−−+    | |
| |            |                                                                     +−−−>|   Function 2    |    | |
| |            |                                                                          +−−−−−−−−−−−−−−−−−+    | |
| |            |                                                                          | [[Environment]] |>−+ | |
| |            |                                                                          +−−−−−−−−−−−−−−−−−+  | | |
| |            |                                                                                               | | |
| |            +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+ | |
| |                                                                                                              | |
| +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+ |
|                                                                                                                  |
+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+

这意味着当我们调用funcs[1]()时,为它创建了一个新环境,它的[[Outer]]设置为函数的[[Environment]],就在return i之前我们有:

               +−−−−−−−−−−−−−−+
current env>−−>| Call to      |
               | `funcs[1]()` |
               | Env Object   |
               +−−−−−−−−−−−−−−+
               | [[Outer]]    |>−−+
               +−−−−−−−−−−−−−−+   |
                                  |    
                                  |    
             +−−−−−−−−−−−−−−−−−−−−+       +−−−−−−−−−−−−−−−−+
             |                            | `example` call |
             |                       +−−−>| Env Object     |
             |                       |    +−−−−−−−−−−−−−−−−+
             |                       |    | [[Outer]]      |>−−−>(The env obj for when `example` was created.)
             |                       |    | i: 3           |
             |                       |    | funcs          |>−+
             |                       |    +−−−−−−−−−−−−−−−−+  |  +−−−−−−−−−−−+
             |                       |                        +−>|  (array)  |
             \     +−−−−−−−−−−−−−−+  |                           +−−−−−−−−−−−+
  +−−−−−−−−−−−+−−−>| Iteration 1  |  |                           | length: 3 |       +−−−−−−−−−−−−−−−−−+
  |                | Env Object   |  |                           | 0         |>−−−−−>|   (function)    |
  |                +−−−−−−−−−−−−−−+>−+                           | 1         |>−−−+  +−−−−−−−−−−−−−−−−−+
  |                | [[Outer]]    |                              | 2         |>−+ |  | [[Environment]] |>...
  |                +−−−−−−−−−−−−−−+                              +−−−−−−−−−−−+  | |  +−−−−−−−−−−−−−−−−−+
  |                                                                             | |                     
  |                                                                             | |  +−−−−−−−−−−−−−−−−−+
  |                                                                             | +−>|   (function)    |
  |                                                                             |    +−−−−−−−−−−−−−−−−−+
  |                                                                             |    | [[Environment]] |>−−−−+
  |                                                                             |    +−−−−−−−−−−−−−−−−−+     |
  |                                                                             |                            |
  |                                                                             |    +−−−−−−−−−−−−−−−−−+     |
  |                                                                             +−−−>|   (function)    |     |
  |                                                                                  +−−−−−−−−−−−−−−−−−+     |
  |                                                                                  | [[Environment]] |>... |
  |                                                                                  +−−−−−−−−−−−−−−−−−+     |
  |                                                                                                          |
  |                                                                                                          |
  +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+

所以函数查找i时,在当前环境中没有找到,在第一个[[Outer]]环境中也没有找到,但是确实找到了在 second [[Outer]] 环境中,值为 3,所以这就是它使用的值。