Одним из самых мощных инструментов JavaScript'а считаются возможность создавать замыкания — это такой приём, когда наша область видимости всегда имеет доступ к внешней области, в которой она была объявлена. Собственно, единственный механизм работы с областями видимости в JavaScript — это функции: т.о. объявляя функцию, вы автоматически реализуете замыкания.
function Counter(start) {
var count = start;
return {
increment: function() {
count++;
},
get: function() {
return count;
}
}
}
var foo = Counter(4);
foo.increment();
foo.get(); // 5
В данном примере Counter возвращает два замыкания: функции increment и get. Обе эти функции сохраняют ссылку на область видимости Counter и, соответственно, имеют доступ к переменной count из этой самой области.
Поскольку в JavaScript нельзя присваивать или ссылаться на области видимости, заполучить count извне не представляется возможным. Единственным способом взаимодействовать с ним остается использование двух замыканий.
var foo = new Counter(4);
foo.hack = function() {
count = 1337;
};
В приведенном примере мы не изменяем переменную count в области видимости Counter, т.к. foo.hack не объявлен в данной области. Вместо этого будет создана или перезаписана глобальная переменная count;
Часто встречается ошибка, когда замыкания используют внутри циклов, передавая переменную индекса внутрь.
for(var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
Данный код не будет выводить числа с 0 до 9, вместо этого число 10 будет выведено десять раз.
Анонимная функция сохраняет ссылку на i и, когда будет вызвана функция console.log, цикл for уже закончит свою работу, а в i будет содержаться 10.
Для получения желаемого результата необходимо создать копию переменной i.
Для того, чтобы скопировать значение индекса из цикла, лучше всего использовать анонимную функцию как обёртку.
for(var i = 0; i < 10; i++) {
(function(e) {
setTimeout(function() {
console.log(e);
}, 1000);
})(i);
}
Анонимная функция-обертка будет вызвана сразу же, и в качестве первого аргумента получит i, значение которой будет скопировано в параметр e.
Анонимная функция, которая передается в setTimeout, теперь содержит ссылку на e, значение которой не изменяется циклом.
Еще одним способом реализации является возврат функции из анонимной функции-обертки, поведение этого кода будет таким же, как и в коде из предыдущего примера.
for(var i = 0; i < 10; i++) {
setTimeout((function(e) {
return function() {
console.log(e);
}
})(i), 1000)
}
Замечание от перев. Переменную
eможно тоже назватьi, если вы хотите: это не поменяет поведения кода — внутренняя переменнаяiвсё так же будет копией внешней переменной