Допустим, вам нужна функция, которую вы можете использовать для возврата уникального значения "id", которое будет использоваться при создании новых элементов DOM. Теперь в чем-то вроде Java вы можете создать класс с внутренним частным счетчиком, а затем создать метод, который добавляет счетчик к некоторой строке префикса. Хорошо, в Javascript:
var getId = (function() {
var counter = 0;
return function() {
return "prefix" + counter++;
};
})();
Теперь переменная "getId" привязана к функции, которая создана другой функцией и создана таким образом, что она имеет постоянную переменную для использования между вызовами. Точно так же, если бы я хотел иметь семейство функций getId (скажем, по одной для каждого типа элемента DOM, который я мог бы добавить), я мог бы сделать это:
var getIdFunc = function(prefix) {
var counter = 0;
return function() {
return prefix + counter++;
};
};
var getId = {
'div': getIdFunc('div'),
'span': getIdFunc('span'),
'dl': getIdFunc('dl'),
// ...
};
Теперь я могу позвонить getId.div()
, чтобы получить новое значение "id" для нового <div>
. Функция была создана путем вызова функции, которая предоставляет два значения , спрятанные в замыкании: строку префикса (переданную в качестве аргумента) и счетчик (var
, объявленный в области замыкания).
Как только вы привыкнете к этому, объект станет настолько гибким и полезным, что вы почувствуете боль при возвращении в среду без него.
О, и вот совет, который поможет вам не вмешиваться в StackOverflow, если вы попробуете это: это проблема, которая всплывает постоянно:
for (var i = 0; i < 10; ++i) {
var id = "foo" + i;
var element = document.getElementById(id);
element.onclick = function() {
alert("hello from element " + i);
};
}
В чем здесь проблема? Ну, эта переменная "i", на которую ссылается эта функция, является "i" из области, в которой выполняется этот цикл. Эта переменная, как вы заметите, увеличивается с помощью цикла (да, верно?). Итак, каждая из этих маленьких функций, созданных и назначенных в качестве обработчиков событий, будет совместно использовать той же самой единственной переменной "i" в области закрытия. К сожалению! Решение состоит в том, чтобы сделать что-то вроде этого:
for (var i = 0; i < 10; ++i) {
var id = "foo" + i;
var element = document.getElementById(id);
element.onclick = (function(iCopy) {
return function() {
alert("hello from element " + iCopy);
};
})(i);
}
Мы превращаем копию внешнего «i» в собственную область замыкания, поэтому теперь у каждого обработчика событий есть свой собственный!
Подводя итог: техника использования замыканий появляется все безумное время , как только вы к этому привыкнете. Это не бесплатный билет в новую страну чудес безошибочного программирования; не пойми меня неправильно. Это, однако, очень полезная и гибкая парадигма.