2.1.1 闭包

闭包的概念在编程理论中经常出现,闭包在函数式语言中普遍存在。与JavaScript中闭包的概念一样,闭包通常情况下指一个特殊的函数或方法,内里绑定了函数内部引用的所有变量。这个函数或方法把它引用的所有内容都放在一个上下文中闭合包裹起来。

这里我们要注意,当使用函数/过程时,影响运行结果的输入,除了传入的参数外,还有自由变量(Free)。自由变量是根据上下文能确定的变量,在函数中既不是参数,也不是局部变量(参数和局部变量被称为约束变量<Bound>)。

闭包的关键在于将被引用的自由变量和函数绑定在一起,即使离开了创造它们的环境也不例外。这些自由变量直到所有已知引用都销毁后,才会被垃圾回收机制销毁,这也是某些递归调用超出调用限制(Maximum call stack size exceeded)的一种原因。

除了函数式语言中的闭包概念,我们在其他语言中也会接触到闭包,如Groovy中的闭包类型(见代码清单2-1)和SICP书中提到的数学上的闭包,它们都是闭包理论的表达和解读方式。

代码清单2-1 Groovy闭包和JavaScript闭包


// Groovy闭包的样式
{ -> item++ }
{ String x, int y ->
  println "${x} value ${y}"
}

// 闭包在Groovy中是 groovy.lang.Closure类的实例,可以用Closure的泛型来指定返回类型
Closure<Boolean> isTextFile = {
  File it -> it.name.endsWith('.txt')
}

// JavaScript闭包实现单例
const Singleton = function(storeName) {
  this.store = storeName;
}

Singleton.getInstance = (function(storeName){
  var instance;
  return function(storeName) {
    if (!instance) {
      instance = new Singleton(storeName);
    }
    return instance
  }
})();

const a = Singleton.getInstance('storageA');
const b = Singleton.getInstance('storageB');   // Singleton {store: "storageA"}

闭包是成员变量的早期实现形式,它实现了代码工程化中重要的封装功能。闭包契合推迟执行的编程原则,可以较好地改变部分被执行的环境代码的生命周期,我们在2.2.1节介绍惰性求值和thunk函数时会介绍闭包的更多应用。