闭包
函数可以作为值使用,而且其局部绑定会在每次函数调用时重新创建,由此引出一个值得我们探讨的问题:如果函数已经执行结束,那么这些由函数创建的局部绑定会如何处理呢?
下面的示例代码展示了这种情况。代码中定义了函数wrapValue
,该函数创建了一个局部绑定localVariable
,并返回一个函数,用于访问并返回局部绑定localVariable
。
function wrapValue(n) {
let local = n;
return () => local;
}
let wrap1 = wrapValue(1);
let wrap2 = wrapValue(2);
console.log(wrap1());
// → 1
console.log(wrap2());
// → 2
这是允许的并且按照您的希望运行 - 绑定的两个实例仍然可以访问。 这种情况很好地证明了一个事实,每次调用都会重新创建局部绑定,而且不同的调用不能覆盖彼此的局部绑定。
这种特性(可以引用封闭作用域中的局部绑定的特定实例)称为闭包。 引用来自周围的局部作用域的绑定的函数称为(一个)闭包。 这种行为不仅可以让您免于担心绑定的生命周期,而且还可以以创造性的方式使用函数值。
我们对上面那个例子稍加修改,就可以创建一个可以乘以任意数字的函数。
function multiplier(factor) {
return number => number * factor;
}
let twice = multiplier(2);
console.log(twice(5));
// → 10
由于参数本身就是一个局部绑定,所以wrapValue
示例中显式的local
绑定并不是真的需要。
考虑这样的程序需要一些实践。 一个好的心智模型是,将函数值看作值,包含他们主体中的代码和它们的创建环境。 被调用时,函数体会看到它的创建环境,而不是它的调用环境。
这个例子调用multiplier
并创建一个环境,其中factor
参数绑定了 2。 它返回的函数值,存储在twice
中,会记住这个环境。 所以当它被调用时,它将它的参数乘以 2。