Поскольку функция, созданная в цикле , закрывает более , который активен, когда он был создан. Эта лексическая среда является (концептуально) объектом, который содержит локали, определенные внутри него (и некоторые другие вещи), включая переменную 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
передачи контура, как хорошо): A нового объекта среды для следующий итерация создается, а значение 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]()
, в ENVIRO для его вызова создается, а его среда [[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' not 'let', он всегда будет возвращать '3' – zyMacro