После прочтения различных решений я хотел бы добавить, что причина, по которой эти решения работают, заключается в том, чтобы опираться на концепцию scope scope . Это способ, которым JavaScript разрешает переменную во время выполнения.
- Каждое определение функции формирует область видимости, состоящую из всех локальных
переменные, объявленные
var
и его arguments
.
- Если у нас есть внутренняя функция, определенная внутри другой (внешней) функции, это
образует цепочку и будет использоваться во время исполнения
- Когда функция выполняется, среда выполнения оценивает переменные путем поиска в цепочке областей действия . Если переменная может быть найдена в определенной точке цепочки, она прекращает поиск и использует ее, в противном случае она продолжается до достижения глобальной области видимости, которая принадлежит
window
.
В исходном коде:
funcs = {};
for (var i = 0; i < 3; i++) {
funcs[i] = function inner() { // function inner's scope contains nothing
console.log("My value: " + i);
};
}
console.log(window.i) // test value 'i', print 3
При выполнении funcs
цепочка областей действия будет function inner -> global
. Поскольку переменная i
не может быть найдена в function inner
(ни объявлена с использованием var
, ни передана в качестве аргументов), она продолжает поиск до тех пор, пока значение i
не будет в конечном итоге найдено в глобальной области видимости window.i
.
Обернув его во внешнюю функцию, либо явно определите вспомогательную функцию, например harto did, либо используйте анонимную функцию, такую как Bjorn did:
funcs = {};
function outer(i) { // function outer's scope contains 'i'
return function inner() { // function inner, closure created
console.log("My value: " + i);
};
}
for (var i = 0; i < 3; i++) {
funcs[i] = outer(i);
}
console.log(window.i) // print 3 still
Когда будет выполнено funcs
, теперь цепочка областей действия будет function inner -> function outer
. На этот раз i
можно найти в области видимости внешней функции, которая выполняется 3 раза в цикле for, каждый раз значение i
связывается правильно. Он не будет использовать значение window.i
при внутреннем выполнении.
Более подробно можно найти здесь
Она включает в себя распространенную ошибку при создании замыкания в цикле, как то, что у нас здесь, а также зачем нам нужно замыкание и соображение производительности.