因爲在循環內創建的函數關閉了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