关于 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
在其初始化程序中如何处理 let
,funcs
中的每个条目都有自己的词法环境,因此也有自己的 i
副本。
当您调用其中一个函数时,会创建一个新的环境对象,其 "outer" 环境设置为附加到该函数的环境。当您在函数代码中引用 i
时,它首先查看函数的环境,如果在那里找不到 i
,它会查看外部环境——找到它的地方(在本例中)并使用它。
在您说过的评论中
If you use 'var' not 'let', it will always return '3'
完全正确。使用 var
,i
将被提升到与 for
循环所在的函数关联的环境对象(或者全局函数,如果这是全局代码)。所以在循环中创建的所有函数共享相同的 i
,当你调用它们时,它的值是 3
.
这是let
/const
和var
的本质区别之一:let
和const
有块作用域,而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
的巧妙处理(事实上,let
和 const
在 for
循环体):为 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
,所以这就是它使用的值。
var funcs=[];
for(let i=0;i<3;i++){
funcs[i]=function(){
return i;
}
}
alert(funcs[1]);
alert(funcs[1]());
function (){
return i;
}
第二个像这样
1
但是我不知道为什么funcs[1]可以执行不报错'i is undefined';
因为在循环中创建的函数 关闭了 创建时处于活动状态的 lexical environment。该词法环境(概念上)是一个对象,其中包含在其中定义的局部变量(以及其他一些东西),包括 i
变量,在本例中是为循环体的特定迭代创建的变量(因为非常特殊的方式 for
在其初始值设定项中处理 let
声明)。这是"closure,"核心技术之一的概念JavaScript。即使执行超出给定词法环境关联的范围(函数 returns,我们继续循环的下一次迭代,等等),如果任何东西仍然引用该环境对象,就像它赖以生存的所有物体一样。
由于 for
在其初始化程序中如何处理 let
,funcs
中的每个条目都有自己的词法环境,因此也有自己的 i
副本。
当您调用其中一个函数时,会创建一个新的环境对象,其 "outer" 环境设置为附加到该函数的环境。当您在函数代码中引用 i
时,它首先查看函数的环境,如果在那里找不到 i
,它会查看外部环境——找到它的地方(在本例中)并使用它。
在您说过的评论中
If you use 'var' not 'let', it will always return '3'
完全正确。使用 var
,i
将被提升到与 for
循环所在的函数关联的环境对象(或者全局函数,如果这是全局代码)。所以在循环中创建的所有函数共享相同的 i
,当你调用它们时,它的值是 3
.
这是let
/const
和var
的本质区别之一:let
和const
有块作用域,而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
的巧妙处理(事实上,let
和 const
在 for
循环体):为 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
,所以这就是它使用的值。