闭包

函数可以作为值使用,而且其局部绑定会在每次函数调用时重新创建,由此引出一个值得我们探讨的问题:如果函数已经执行结束,那么这些由函数创建的局部绑定会如何处理呢?

下面的示例代码展示了这种情况。代码中定义了函数wrapValue,该函数创建了一个局部绑定localVariable,并返回一个函数,用于访问并返回局部绑定localVariable

  1. function wrapValue(n) {
  2. let local = n;
  3. return () => local;
  4. }
  5. let wrap1 = wrapValue(1);
  6. let wrap2 = wrapValue(2);
  7. console.log(wrap1());
  8. // → 1
  9. console.log(wrap2());
  10. // → 2

这是允许的并且按照您的希望运行 - 绑定的两个实例仍然可以访问。 这种情况很好地证明了一个事实,每次调用都会重新创建局部绑定,而且不同的调用不能覆盖彼此的局部绑定。

这种特性(可以引用封闭作用域中的局部绑定的特定实例)称为闭包。 引用来自周围的局部作用域的绑定的函数称为(一个)闭包。 这种行为不仅可以让您免于担心绑定的生命周期,而且还可以以创造性的方式使用函数值。

我们对上面那个例子稍加修改,就可以创建一个可以乘以任意数字的函数。

  1. function multiplier(factor) {
  2. return number => number * factor;
  3. }
  4. let twice = multiplier(2);
  5. console.log(twice(5));
  6. // → 10

由于参数本身就是一个局部绑定,所以wrapValue示例中显式的local绑定并不是真的需要。

考虑这样的程序需要一些实践。 一个好的心智模型是,将函数值看作值,包含他们主体中的代码和它们的创建环境。 被调用时,函数体会看到它的创建环境,而不是它的调用环境。

这个例子调用multiplier并创建一个环境,其中factor参数绑定了 2。 它返回的函数值,存储在twice中,会记住这个环境。 所以当它被调用时,它将它的参数乘以 2。