要了解闭包,您首先需要了解作用域。范围是变量的生命周期。在作用域内声明的变量诞生了,当作用域结束时它就消失了。例如:
{ // scope begins
var x = 0; // variable x is born
var y = 1; // variable y is born
} // scope ends, x and y both die
现在,有一种方法可以让变量在声明的作用域结束后仍保持活动状态。简而言之,这是一种变量可以欺骗死亡的方式。变量欺骗死亡的方式是通过闭包。
考虑另一个范围内的范围。内部范围可以访问外部范围内声明的所有变量。但是,外部作用域无法访问内部作用域内声明的任何变量。
{ // the outer scope has access to x and y only
var x = 0;
var y = 1;
{ // the inner scope has access to x, y and z
var z = 2;
}
}
内部作用域可以访问z,因为它是在内部作用域中声明的。但是,它也可以访问x 和y,因为这两个变量位于内部作用域的词法环境(即父作用域)中。
现在考虑如果我们可以将这个内部作用域移到外部作用域之外会发生什么。
{ // the outer scope has access to x and y only
var x = 0;
var y = 1;
{ // the inner scope has access to x, y and z
var z = 2;
} ---+
} |
|
{ <------+ // the inner scope moved outside of the outer scope
var z = 2;
}
在这种情况下,外部作用域结束。因此,x 和y 应该随着外部作用域而死。但是,它们不能死,因为它们仍然是内部作用域所需要的。当内部作用域被移到外部作用域之外时,它成为变量x 和y(这是它的上值)的闭包,并且只要这些变量本身存在,它就会使这些变量保持活动状态。这就是闭包的一般概念。
现在考虑以下代码:
function startAt(x) {
return incrementBy;
function incrementBy(y) {
return x + y;
}
}
var closure1 = startAt(1);
var closure2 = startAt(5);
这里,incrementBy 函数是一个内部函数。因此,它可以访问属于startAt 函数的变量x。然而,当我们从startAt 返回incrementBy 时,我们将内部函数移到外部函数之外。因此,虽然x应该随着startAt的回归而死去,但它仍然活着,因为incrementBy需要它。
因此,incrementBy 是一个闭包,因为它关闭了变量 x 并在它自己存在的时候让它保持活动状态;而变量x被称为闭包incrementBy的上值。