Всякий раз, когда вы видите ключевое слово function в другой функции, внутренняя функция имеет доступ к переменным во внешней функции.
function foo(x) {
var tmp = 3;
function bar(y) {
console.log(x + y + (++tmp)); // will log 16
}
bar(10);
}
foo(2);
Это всегда будет записывать 16, потому что bar
может получить доступ к x
, который был определен как аргумент foo
, а также tmp
из foo
. Он также может получить доступ к tmp
.
Что является замыканием. Функция не должна возвращать , чтобы называться замыканием. Простое обращение к переменным за пределами вашей непосредственной лексической области создает закрытие .
function foo(x) {
var tmp = 3;
return function (y) {
console.log(x + y + (++tmp)); // will also log 16
}
}
var bar = foo(2); // bar is now a closure.
bar(10);
Вышеуказанная функция также будет записываться в журнал 16, поскольку bar
все еще может ссылаться на x
и tmp
, даже если он больше не находится внутри области.
Однако, поскольку tmp
все еще висит внутри bar
, он также увеличивается. Он будет увеличиваться каждый раз при вызове bar
.
Простейшим примером замыкания является следующее:
var a = 10;
function test() {
console.log(a); // will output 10
console.log(b); // will output 6
}
var b = 6;
test();
При вызове функции JavaScript новый контекст выполнения создано. Вместе с аргументами функции и родительским объектом этот контекст выполнения также принимает все переменные, объявленные вне него (в приведенном выше примере «a» и «b»).
Можно создать более одной функции закрытия, либо путем возврата их списка, либо путем установки их на глобальные переменные. Все они будут ссылаться на те же x
и те же tmp
, что они не делают своих собственных копий.
Здесь число x
- это буквальное число. Как и в других литералах в JavaScript, когда вызывается foo
, число x
копируется в foo
как его аргумент x
.
С другой стороны, JavaScript всегда использует ссылки при работе с объектами. Если, скажем, вы вызвали foo
с объектом, то его замыкание ссылается на этот исходный объект!
function foo(x) {
var tmp = 3;
return function (y) {
console.log(x + y + tmp);
x.memb = x.memb ? x.memb + 1 : 1;
console.log(x.memb);
}
}
var age = new Number(2);
var bar = foo(age); // bar is now a closure referencing age.
bar(10);
Как и ожидалось, каждый вызов bar(10)
будет увеличиваться x.memb
. Нельзя ожидать, что x
просто ссылается на тот же объект, что и переменная age
! После пары звонков в bar
, age.memb
будет 2! Эта ссылка является основой для утечек памяти с объектами HTML.