В этом посте будет просто объяснено, как работают Javascript Closures. Мы рассмотрим эти темы и часто задаваемые вопросы:
- Что такое закрытие Javascript
- В чем причина названия «Закрытие»
- На самом деле просмотр замыканий в отладчике
- Как рассуждать о замыканиях при кодировании
- Наиболее распространенные подводные камни его использования
Простой пример (ошибка включена)
Самый простой способ понять замыкания — это понять, какую проблему они пытаются решить. Давайте рассмотрим простой пример кода со счетчиком, который увеличивается в 3 раза внутри цикла.
Но внутри цикла что-то асинхронное выполняется со счетчиком. Может случиться так, что был сделан серверный вызов, в этом случае давайте просто вызовем
setTimeout
это, чтобы отложить его выполнение до истечения времени ожидания:
// define a function that increments a counter in a loop function closureExample() { var i = 0; for (i = 0; i< 3 ;i++) { setTimeout(function() { console.log('counter value is ' + i); }, 1000); } } // call the example function closureExample();
Некоторые вещи, которые нужно иметь в виду:
- переменная i существует в области действия
closureExample
функции и не доступна извне - во время цикла по переменной
console.log
оператор не выполняется сразу console/log
будет выполняться асинхронно 3 раза и только после истечения 1 секунды- Это означает, что установлены 3 таймаута, а затем
closureExample
возвращается почти сразу
Что приводит нас к основному вопросу об этом коде:
Когда выполняется анонимная функция ведения журнала, как она может получить доступ к переменной «i»?
Вопрос возникает с учетом того, что:
- переменная я не был передан в качестве аргумента
- когда
console.log statement
выполняется,closureExample
функция давно закончилась.
Так что же такое закрытие?
Когда функция регистрации передается setTimeout
методу, механизм Javascript обнаруживает, что для функции, которая будет выполняться в будущем, потребуется ссылка на переменную i.
Чтобы решить эту проблему, движок сохраняет ссылку на эту переменную для последующего использования и сохраняет эту ссылку в контексте выполнения специальной области действия.
Такая функция с «памятью» о среде, в которой она была создана, называется просто: Закрытие .
Почему имя Закрытие тогда?
Это связано с тем, что функция проверяет свою среду и закрывает переменные, которые необходимо запомнить для дальнейшего использования. Ссылки на переменные закрыты в специальной структуре данных, доступ к которой может получить только сама среда выполнения Javascript.
Есть ли способ увидеть закрытие?
Самый простой способ — использовать отладчик Chrome Developer Tools и установить точку останова в строке 7 приведенного выше фрагмента кода.
Когда наступит первый тайм-аут, замыкание отобразится на панели «Переменные области действия» отладчика:
Как мы видим, замыкание — это просто простая структура данных со ссылками на переменные, которые функция должна «запомнить», в данном случае это переменная i.
Но тогда, где ловушка?
Мы можем ожидать, что журнал выполнения покажет:
counter value is 0 counter value is 1 counter value is 2
Но настоящий журнал выполнения на самом деле:
counter value is 3 counter value is 3 counter value is 3
Это не ошибка, это способ закрытия. Функция регистрации является замыканием (или имеет укупорочное средство , как этот термин используется в обоих направлениях) , содержащая ссылку на переменный я.
Это ссылка, а не копия, поэтому происходит следующее:
- цикл заканчивается, и значение переменной i равно 3
- только позже истечет первый тайм-аут, и функция регистрации запишет значение 3
- истекает второй тайм-аут, и функция регистрации все еще регистрирует 3 и т. д.
Как получить другое значение счетчика для асинхронной операции?
Это может быть сделано, например, путем создания отдельной функции для запуска асинхронной операции. Следующий фрагмент даст ожидаемый результат:
function asyncOperation(counter) { setTimeout(function() { console.log('counter value is ' + counter); }, 1000); } function otherClosureExample() { var i = 0; for (i = 0; i < 3 ;i++) { asyncOperation(i); } } otherClosureExample();
Это работает, потому что при вызове asyncOperation
копии создается значение счетчика, и регистрация «закрывает» это скопированное значение. Это означает, что каждый вызов функции регистрации будет видеть другую переменную со значениями 0, 1, 2.
Вывод
Закрытия Javascript — это мощная функция, которая в основном прозрачна при повседневном использовании языка.
Они могут быть удобным способом уменьшить количество параметров, передаваемых в функцию.
Но главным образом тот факт, что закрытые переменные недоступны
извне функции, делает замыкания хорошим способом достижения «закрытых» переменных и инкапсуляции в Javascript.
В основном функция «просто работает», а функции Javascript прозрачно запоминают любые переменные, необходимые для будущего выполнения, удобным способом.
Но остерегайтесь ловушки: замыкания хранят ссылки, а не копии (даже примитивных значений), поэтому убедитесь, что это действительно предполагаемая логика.