Замыкание (Closure)
Что такое замыкание?
Где применяется замыкание?
Что означает лексическая области видимости функции?
При выполнении функции используется та область видимости (Scope) переменных которая существовала при объявлении этой функции - лексическая область видимости.
Пример:
var func = function(){
var i = 10;
return function(){
return i;
};
};
var anotherFunc = function(){
var i = 20;
console.log(func()());
};
anotherFunc(); // в консоле будет 10, т.к. у внут функции func - лексическая область видимости внутри функции func
Замыкание - это особый объект который сочетает в себе две вещи: функцию и окружение, в которой функция была создана.
У нас есть внешняя функция у которой объявлена переменная, и есть внутряняя функция которая использует эту переменную. Говорят что внутряняя функция замыкает в себе переменную внешней функции. Она не дает удалить эту переменную сборщиком мусора, даже когда внешняя функция отработала и была удалена.
;
Пример использования замыкания:
function counter(){
var count = 0; // это не внутренее свойства, а просто ссылка на область в памяти.
return {
next: function(){
console.log(++count);
}
}
}
var c = counter();
c.next(); //1
c.next(); //2
Что происходит на самом деле. При выполнении функции создается объект LexicalEnvironment (функция тоже на самом деле объект, который можно возвращать, присваивать, передавать как параметр), который хранит все свойства и агрументы функции. Также он получает скрытое свойство [[Scope]] (next.[[Scope]] = counter), которое ссылается на лексическое окружение в котором была вызвана функция. И при поиске свойства - сначало оно ищется внутри своего объекта, потом по ссылке [[Scope]] во внешнем окружении, и т.д.
С помощью замыкания мы можем реализовать инкапсуляцию (скрытие внутренней реализации):
function counter(){
var count = 0;
return {
get: function(){
return count;
},
set: function(c){
count = c;
}
}
};
var myCounter = counter();
console.log(myCounter.count); // undefined, нельзя обратится напрямую к переменной функции
myCounter.set(10);
console.log(myCounter.get()); //10
Еще распространенная ошибка это замыкание и циклы:
var arr = [];
for(var i = 0; i < 3; i++){
arr[i] = function(){
console.log(i);
};
}
arr[0](); //мы получим 3, а не 0
Было создано три замыкания, но все они были созданы с одним и тем же контекстом исполнения (окружением). К моменту выполнения функции цикл уже давно отработал, а значит переменная i (одна и та же для всех трех замыканий) указывает на последний элемент массива (3).
Методы решения:
Метод 1
var arr = [];
for(let i = 0; i < 3; i++){
arr[i] = function(){
console.log(i);
};
}
arr[0](); //мы получим 3, а не 0
Метод 2
var arr = [];
for(var i = 0; i < 3; i++){
(function(j){
arr[j] = function(){
console.log(j);
};
})(i);
}
arr[0](); //мы получим 3, а не 0
Метод 3
//todo
Материал взят из GetInstance - use strick