The safest way to try to get what you want is to try to deserve what you want

明明白白-JavaScript中的闭包

Posted on By Frank·J

文章总结于 《JavaScript权威指南》

什么是闭包?

函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性在计算机科学文献中被称为“闭包”

以上是标准的解释,需要你对变量作用域以及作用域链的概念有一定的了解,这样理解起来就会得心应手。那如果你不在意那些繁文缛节,想要以最简单的方式搞明白什么是闭包,那么请继续读下去吧。

PS: JavaScript 中的所有的函数都是闭包:它们都是对象,它们都关联到作用域链。

嵌套函数的词法作用域

var scope = 'global scope';   // 全局变量
function checkScope() {
  var scope = 'local scope';  // 局部变量
  function f() {
    return scope;             // 在作用域中返回这个值
  }
  return f();
}
checkScope()                  // 返回值 ???

以上函数调用的返回值是什么呢?聪明的你应该非常清楚调用 checkScope() 会返回 ‘local scope’,下面对这段代码做一点小小的改动:

var scope = 'global scope';   // 全局变量
function checkScope() {
  var scope = 'local scope';  // 局部变量
  function f() {
    return scope;             // 在作用域中返回这个值
  }
  return f;
}
checkScope()()                // 返回值 ???

揭晓答案之前,我们先来回想一下词法作用域的基本规则:JavaScript 函数的执行用到了作用域链,这个 作用域链是函数定义的时候创建的 ;嵌套函数 f() 定义在这个作用域链里,其中变量 scope 一定是局部变量,不管在何时何地执行函数 f(),这种绑定依然有效,因此最后一行代码的返回值是 “local scope”

一句话解释闭包

函数定义时的作用域链在函数执行时依然有效

深入了解闭包

PS: 以下内容可能引起你的不适,请酌情选择阅读

  • 函数的调用:在 JavaScript 中每次调用函数的时候,都会创建一个新的对象用来保存局部变量,把这个对象添加至作用域链中。
  • 函数返回:当函数返回的时候,就从作用域中将这个绑定变量的对象删除
  • 垃圾回收:如果不存在嵌套函数,也没有其他引用指向这个绑定对象,该对象就会被垃圾回收掉
  • 其它情况:如果定义了嵌套函数,每个嵌套函数都会有各自对用的作用域,并且这个作用域链接指向一个变量绑定对象,如果这些嵌套的函数对象在外部函数中保存下来,那么它们也会和所指向的变量绑定对象一样被当作垃圾回收;但是如果这个函数定义了嵌套函数,并且讲它作为返回值返回或者存储在某处的属性里,这时就会有一个外部引用指向这个嵌套的函数,它就不会被当做垃圾回收,并且它所指向的变量绑定对象也不会被当做垃圾回收。