因为在循环内创建的函数关闭了lexical environment它在创建时处于活动状态。这个词汇环境是(概念上)一个对象,它包含了定义在其中的局部变量(以及其他一些东西),包括变量i
,在这种情况下是为循环体的特定迭代而创建的变量(由于非常特殊的方式for
在其初始化程序中处理let
声明)。这是JavaScript的核心技术之一“闭包”的概念。即使执行超出了范围,一个给定的词法环境与(一个函数返回,我们继续下一个循环迭代等)相关联,如果任何东西仍然有一个对该环境对象的引用,就像所有对象一样继续生活。
因为如何for
在其初始化处理let
的,在funcs
每个条目都有自己的词汇环境,因而它的i
自己的副本。
当您调用其中一个函数时,会创建一个新的环境对象,并将其“外部”环境设置为附加到该函数的环境。当您在功能代码中引用i
时,它首先查看该功能的环境,如果在该处找不到i
,它会在外部环境 —中查找它(在本例中)并在其中使用该环境。
在评论你说
如果使用 '变种' 而不是 '让',它总是会返回 '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
循环开始:创建一个新的每一次迭代环境对象到位“当前”之一,与前一个作为其“外部”环境。一个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]] |>−−−−−+
| +−−−−−−−−−−−−−−−−−+ |
| |
+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+
现在,这是的let
在for
初始化巧妙的处理工作(事实上,巧妙处理的let
和const
一个for
循环体内部以及):一个新环境对象下一个迭代被创建,并且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]]
环境找到它,但它确实找到它在秒[[Outer]]
环境,价值3
,所以这是它使用的价值。
如果您使用'var'而不是'let',它将始终返回'3' – zyMacro