Дополнительно
JavaScript закрытие внутри петли – простой практический пример
в
var funcs = [];
// let's create 3 functions
for (var i = 0; i < 3; i++) {
// and store them in funcs
funcs[i] = function() {
// each should log its value.
console.log("My value: " + i);
};
}
for (var j = 0; j < 3; j++) {
// and now let's run each one to see
funcs[j]();
}
в
Он выводит это:
моя ценность: 3 Моя ценность: 3 Моя ценность: 3
В то время как я'd как он для вывода:
моя ценность: 0 Мои ценности: 1 Моя ценность: 2
Та же проблема возникает, когда задержка в управлении функция вызывается с помощью прослушивателей событий:
в
var buttons = document.getElementsByTagName("button");
// let's create 3 functions
for (var i = 0; i < buttons.length; i++) {
// as event listeners
buttons[i].addEventListener("click", function() {
// each should log its value.
console.log("My value: " + i);
});
}
<button>0</button>
<br />
<button>1</button>
<br />
<button>2</button>
в
... или асинхронного кода, например, с помощью обещаний:
в
// Some async wait function
const wait = (ms) => new Promise((resolve, reject) => setTimeout(resolve, ms));
for (var i = 0; i < 3; i++) {
// Log `i` as soon as each promise resolves.
wait(i * 100).then(() => console.log(i));
}
в
Каково же решение этой базовой проблемы?
2680
44
Ну, проблема в том, что переменная "я", в каждом из ваших анонимных функций, привязан к одной и той же переменной за пределами функции.
Классическое решение: затворы
Что вы хотите сделать, это привязать переменную в каждую функцию в отдельный, неизменное значение за пределами функции:
в
в
Поскольку нет области видимости блока в JavaScript - всего лишь масштаб функции упаковки создание функции в новую функцию, вы убедитесь, что стоимость "Я" остается как задумано.
<ч>
Решение № 2015: по каждому элементу
С относительно широкой доступности из массива.прототип.функцию foreach
(в 2015 году), это's стоит отметить, что в тех ситуациях, затрагивающих в первую очередь итерации по массиву значений
.по каждому элементу () обеспечивает чистый, естественный способ получить Идеальное закрытие для каждой итерации. То есть, если вы've получили какой-то массив, содержащий значения (Дом ссылками, объекты и т. д.), И возникает проблема Настройки Вызовы, характерные для каждого элемента, вы можете сделать это:Идея заключается в том, что каждый вызов функции обратного вызова используется с `.цикл foreach будет собственного закрытия. Параметр, передаваемый в обработчик элемента массива, характерные для определенного шага итерации. Если это's используемый в асинхронный обратный вызов, он выиграл'т столкнуться с любым из других обратных вызовов, установленных на других шагах итерации.
Если вы будете работать в jQuery `$.каждый () функция дает вам подобную возможность.
<ч>
Решение ЕС6:
давай
В ECMAScript 6 (ES6 в) вводит новый
дай
иконстантные ключевые слова, которые относятся иначе, чем
Варна основе переменных. Например, в цикле с
Давай-индекс, каждая итерация цикла будет иметь новое значение
я`, где каждое значение определяется внутри цикла, так что ваш код будет работать, как вы ожидаете. Есть много ресурсов, но я'd рекомендую 2ality's блок-обзорный пост как большой источник информации.Остерегайтесь, однако, что IE9-IE11 и края до края 14 поддержка
да
, но получить выше неправильно (они не'т создать новое " я " каждый раз, так что все эти функции будет входить 3, как если бы мы использовалиВАР
). Краем 14, наконец, получает это право.Попробуйте:
в
в
Редактировать (2014):
Лично я думаю, что @Ауст'ы более поздние ответа об использовании
.привязать
это лучший способ, чтобы делать такого рода вещи сейчас. Там's также Ло-тире/подчеркивания'ы_.частичное-когда вы Дон'т необходимость, или хотите возиться с
привязать'ы
параметр thisarg`.Еще один способ, который еще'т еще не упомянул-это использование функции
.прототип.привязать
в
в
Обновление
Как указал @щуриться и @mekdev, вы получите лучшую производительность, создав функцию вне цикла и затем связывая результаты в цикле.
в
в
С помощью сразу-вызывается функция выражения, самый простой и четкий способ заключите переменную индекса:
в
в
Это посылает итератор " я " в анонимной функции, которую мы определяем как
индекс
. Это создает закрытие, где переменная " я " будет сохранен для последующего использования в любом асинхронные функции в жизни.Немного опоздала на вечеринку, но я исследовал этот вопрос сегодня и заметил, что многие ответы не't полностью адрес как на JavaScript относится к области, которая является по существу это сводится к тому.
Так как многие другие упомянули, проблема заключается в том, что внутренняя функция ссылается на те же переменная
i
. Так почему Дон'т мы просто создадим новую локальную переменную каждой итерации, и есть внутренняя ссылка на функцию вместо?в
в
Как и раньше, где каждая внутренняя функция выводится последнее значение для "Я", теперь каждая внутренняя функция просто выводит последнее значение, присвоенное
ilocal
. Но разве'т каждая итерация это's собственноеilocal
?Получается, что's вопрос. Каждая итерация является совместное использование одной и той же области, поэтому каждой итерации после первого-это просто перезапись
ilocal
. От MDN:Повторил для убедительности:
Мы можем убедиться в этом, просмотрев
ilocal
прежде чем мы объявим его в каждой итерации:в
в
Это точно, почему этот баг так сложно. Даже если вы повторное объявление переменной в JavaScript выиграл'т бросить ошибку, и JSLint выиграл'т даже бросить предупреждение. Поэтому лучший способ решить это, чтобы воспользоваться преимуществами блокад, которые, по существу, идея о том, что в JavaScript, внутренние функции имеют доступ к внешним переменным, так как внутренние области, что "приложить" и внешних областях.
Это также означает, что внутренние функции на "удержать" и внешние переменные и сохранить их в живых, даже если внешняя функция возвращает. Чтобы использовать это, мы создаем и вызываем функцию-оболочку, чисто чтобы сделать новую область, объявлять
ilocal в новой области, и вернуть внутреннюю функцию, которая использует
ilocal` (обьяснение ниже):в
в
Создание внутренней функция внутри функции-оболочки придает внутреннюю функцию собственной среде, что только он может открыть, а "закрытие" по. Таким образом, каждый раз, когда мы вызываем функцию-оболочку, мы создаем новую внутреннюю функцию с Это'собственная отдельная среда, гарантируя, что
ilocal
переменные Дон'т сталкиваются и перекрывают друг друга. Несколько незначительных оптимизаций дает окончательного ответа, что многие другие так что пользователи дали:в
в
Обновление
С ЕС6 сейчас мейнстрим, теперь мы можем использовать новые
пусть
ключевое слово, чтобы создать блок-переменные.в
в
Посмотрите, как легко это сейчас! Для получения более подробной информации см. Этот ответ, что моя информация базируется прочь.
С ES6 в настоящее время широко поддерживается, лучший ответ на этот вопрос изменилась. На ES6 обеспечивает
дай
иконстантный
ключевые слова для этой ситуации. Вместо того, чтобы возиться с замыканиями, мы можем просто использоватьПусть
, чтобы установить область видимости переменной цикла такой:в
в
"вал" будет выберите объект, который является специфическим для конкретного поворота петли, и возвращает правильное значение без дополнительных обозначений закрытия. Это, очевидно, значительно упрощает эту проблему.
как const
похож напусть
с дополнительным ограничением, что имя переменной может'т быть отскок на новую ссылку после первоначального назначения.Поддержка браузеров в настоящее время здесь для тех, которые ориентированы на последние версии браузеров.
константный
/да
в настоящее время поддерживаются в последних версиях Firefox, сафари, край и хром. Он также поддерживается в узел, и вы можете использовать его в любом месте, воспользовавшись инструменты для сборки, как Бабель. Вы можете увидеть рабочий пример здесь: http://jsfiddle.net/ben336/rbU4t/2/Документы здесь:
Остерегайтесь, однако, что IE9-IE11 и края до края 14 поддержка
да
, но получить выше неправильно (они не'т создать новое " я " каждый раз, так что все эти функции будет входить 3, как если бы мы использовалиВАР
). Краем 14, наконец, получает это право.Другой способ сказать это состоит в том, что " я " в функции связывается во время выполнения функции, а не в момент создания функции.
При создании закрытия, " я " - это ссылка на переменную, определенную В за пределы, а не копию, как это было при создании закрытия. Это будет оцениваться на момент исполнения.
Большинство других ответов предоставляют способы обойти путем создания другой переменной, которая выиграла't изменить значение для вас.
Просто думал, что я'd-добавить пояснение для ясности. Для решение, лично я'd переход с Harto's так это самый пояснений способ сделать это из ответов здесь. Код написал будет работать, но я'д опт на закрытие завода за необходимости писать кучу комментариев, чтобы объяснить, почему я'м объявлении новой переменной(Фредди и 1800'ы) или у вас странные встроенные закрытие синтаксис(apphacker).
То, что вам нужно, чтобы понять масштабы переменных в JavaScript базируется на функции. Это важная разница, чем скажем на C#, где у вас есть рамки блока, и просто копирование значения переменной внутри цикла for будет работать.
Обернуть его в функцию, которая оценивает возвращение функцией как apphacker'ы ответ будет делать трюк, так как переменная имеет область видимости функции.
Существует также ключевого слова let вместо var, что позволит использовать правила области видимости блока. В этом случае определение переменной внутри цикла for будет делать трюк. Что сказал, Давайте сайта это'т практическое решение из-за совместимости.
в
в
Здесь'ы еще одна вариация на технике, похожими на Бьорн'ы (apphacker), которая позволяет присвоить значение переменной внутри функции, а не передачи его в качестве параметра, что может быть яснее, иногда:
в
в
Обратите внимание, что какую бы технику вы используете, переменной
Index
становится своего рода статическую переменную, обязательно возвращается копия внутреннюю функцию. Т. е., изменения ее значение сохраняется между вызовами. Это может быть очень удобно.Это описывает распространенные ошибки с помощью замыканий в JavaScript.
Функция определяет новые условия
Рассмотрим:
Для каждого времени makeCounter
вызове
{результаты счетчика: 0}в новый объект создается. Кроме того, новый экземпляр объекта obj
создается также ссылаться на новый объект. Таким образом,счетчика 1 и счетчика 2
независимы друг от друга.Застежка на петли
Используя замыкание в цикле сложно.
Рассмотрим:
Заметим, что счетчики[0]
и
счетчики[1]*не* зависит. На самом деле, они работают на тех же
в obj`!Это происходит потому, что существует только один экземпляр объекта obj
общей для всех итераций цикла, возможно, по причинам производительности. Хотя
{счетчик: 0}создает новый объект на каждой итерации, одну и ту же копию
в obj` будет обновлена с ссылка на новый объект.Решение-использовать другой вспомогательной функцией:
Это работает, потому что выделяются локальные переменные в области видимости функции непосредственно, а также функция аргумента переменных новые копии при въезде.
Более подробное обсуждение этого вопроса см. JavaScript закрытие ловушек и использование
Самым простым решением будет,
Вместо использования:
<Н2>попробуйте это покороче</Н2>
нет выбора
без дополнительной для петли
<БР/>
[http://jsfiddle.net/7P6EN/][1]
Здесь'ы простое решение, которое использует
объекту
(работает обратно до IE9):в
в
Печать:
Основная проблема с код, показанный на ФП заключается в том, что " я " никогда не читайте до второй петли. Чтобы продемонстрировать, представьте себе, видя ошибки внутри кода
Ошибки на самом деле не происходит до тех пор, пока нам библиотеки и функции[someIndex]
выполняется
(). Используя эту же логику, очевидно, что ценность " я " тоже не собираются до сих пор либо. Когда заканчивается цикл,
я++приносит
яв значение
3, который приводит в состояние
я &Л; 3сбой и цикл заканчивается. В этот момент,
я- это
3и теперь, когда нам библиотеки и функции[someIndex]()
используется, и " я " оценивается, это 3 - каждый раз.Чтобы обойти это, вы должны оценивать "я", как оно встречается. Отметим, что это уже произошло в виде
нам библиотеки и функции[я]
(где есть 3 уникальные индексы). Есть несколько способов записать это значение. Одна-передать его в качестве параметра в функцию, которая проявляется в нескольких направлениях уже здесь.Другой вариант-создать объект функции, который сможет закрыть над переменной. Это может быть достигнуто таким образом
[
демо jsFiddle
](http://jsfiddle.net/QcUjH/)Функции JavaScript на "закрывают" в рамках они имеют доступ к по декларации, и сохранит доступ к этой области даже в качестве переменных, которые изменяют объем.
в
в
Каждая функция в массиве выше закрывается на глобальном уровне (глобальном, просто потому, что это будет сфера они'вновь объявлен в).
Позднее эти функции вызываются лесозаготовки самое актуальное значение
я
в глобальной области видимости. Что'с магией, и разочарования, закрытие."Мои функции JavaScript закрыть за рамки, в котором они объявлены, и сохранит доступ к этой области даже в качестве значений переменных внутри этой области изменения.&и"
Используя
да
вместоВАР
решает эту проблему путем создания новой области каждый раз, когда цикл for выполняется, создавая отдельной сферы для каждой функции, чтобы закрыть за. Различные другие методы сделать то же самое с дополнительными функциями.в
в
(
пусть
делает переменных в блок. Блоки обозначаются фигурными скобками, но в случае цикла for инициализация переменной, в нашем случае, считается объявленным в скобки.)После прочтения различных решений, я'd, как, чтобы добавить, что причина этих решений будет опираться на концепцию от цепочки области видимости. Это's пути на JavaScript устранить переменной во время выполнения.
ВАР
иаргументы
.В исходном коде:
Когда нам библиотеки и функции
выполняется, цепь объем будет функция внутренняя -> глобальные. Поскольку переменная
яне может быть найден в функция
внутри(ни заявленного, используя
ВАРи не передаются в качестве аргументов), он продолжает искать, пока значение " я " - в конечном итоге нашли в глобальной области видимости, в которой находится окно.я
.Если поместить его в внешней функции либо явно определить вспомогательную функцию, как harto или же использовать анонимную функцию как Бьорн сделал:
Когда нам библиотеки и функции
запускается на выполнение, сейчас сеть области будет функционировать внутреннее -> функция внешней. На этот раз
яможет быть найден во внешнем функция's в рамках которой выполняется 3 раза в цикл, каждый раз имеет значение " я " привязано правильно. Он выиграл't использовать значение
окно.я когда внутренний казнен.Более подробно можно ознакомиться здесь
Оно включает в себя распространенные ошибки в создании застежкой на петли так, что мы имеем здесь, а также, Зачем нужны блокады и рассмотрения производительности.
С новых функций из ES6 область видимости уровне осуществляется:
Код в ОП'ы вопрос заменяется
пусть
вместоВАР
.Я'м удивлен, еще никто не предложил использовать функцию foreach для того, чтобы лучше избежать (повторного)использования локальных переменных. На самом деле, я'м не используя для(ВАР я ...) вообще больше по этому поводу.
// отредактированы с помощью foreach вместо карте.
Этот вопрос действительно показывает историю о JavaScript! Теперь мы можем избегать область видимости функции стрелка и ручка петли непосредственно из узлов DOM с помощью методов объекта.
в
в
в
в кнопки константный = документ.метод getelementsbytagname("и кнопка" - а); Объект .клавиши(кнопки) .карту(я => кнопки[я].метод addEventListener('нажмите', () => В консоли.журнал(я)));
в
В первую очередь, понять, что'ы не так с этим кодом:
Вот когда нам библиотеки и функции в
[массив]
инициализируется, " я " происходит инкремент, массиванам библиотеки и функции
инициализируется и размерФунк
массив будет 3, я = 3,. Теперь, когда нам библиотеки и функции с
[Дж] () называется, он снова с помощью переменнойi
, которая уже увеличивается до 3.Теперь, чтобы решить эту проблему, у нас есть много вариантов. Ниже приведены два из них:
я
сда
или инициализировать индекс новой переменнойс
да` и сделать его равным "я". Поэтому, когда вызов делается, индекс будет использоваться, а сфера ее применения будет в конце после инициализации. И за звонок, индекс вновь будет инициализирован:ВАР нам библиотеки и функции = []; для (ВАР я = 0; я &Л; 3; я++) { пусть индекс = Я; нам библиотеки и функции[я] = функция() { консоль.отчет("Мой значение: на " + индекс); }; } для (ВАР с j = 0; j в < 3; к++) { нам библиотеки и функции[Дж](); }
tempFunc
, который возвращает саму функцию:ВАР нам библиотеки и функции = []; tempFunc функция(я){ функция возврата(){ консоль.журнал("Мой значение: " В + Я); }; } для (ВАР я = 0; я &Л; 3; я++) { нам библиотеки и функции[я] = tempFunc(я); } для (ВАР с j = 0; j в < 3; к++) { нам библиотеки и функции[Дж](); }