在这滥用概念的世界,到处都流传着一些谣言,是时候传播我自己的了。

JavaScript引擎当找到我们定义的函数时,会创建该函数的执行上下文(执行环境),并保存(压栈)作用域内的变量。当要调用函数时,将刚才保存的变量提供给函数使用(出栈),函数正常运行直到所有代码执行完。此刻,执行环境早已不复存在,JavaScript引擎会通知垃圾回收器回收“垃圾变量”。

闭包是阻止垃圾回收器将变量从内存中移除的方法,使得在创建变量的执行环境的外面能够访问到该变量。

这是书上的原话,个人以为这是不准确的说法。故事的主人公不应该是闭包,而是JavaScript引擎。有个关键的东西,叫做计数器,它是该函数执行环境的一个属性,从全局执行环境继承而来,默认为0。我可以猜到如果更改了默认值,JavaScript引擎不会启动垃圾回收器。可惜的是,我们不能直接访问该函数的执行环境。模块化在编程中很重要,用函数的方式实现了模块化。为了让模块重复使用不至于销毁,JavaScript语言有一种人为的间接方法,可以使得JavaScript引擎创建了一个闭包,更改了默认计数,阻止变量被清除。 这个方法不是书上指代的闭包,它与JavaScript语言有关系。

在JavaScript中,访问复杂的结构(数组、函数),都是引用该结构的地址。  

JavaScript引擎一旦发现了引用,就会更改该函数的执行环境的计数器,需要再次使用的变量在内存中就被保留了下来,逻辑上确实是这样,书上也是这样教我的,但我不想就此止步。有引用就能间接地创建一个闭包吗?真的吗?我要做个实验,来证明作者是个混蛋,尽管他们有着十多年的架构经验。

<script type = "text/javascript">
    //  例子1
   var b = 20;
   var my_func = function(){
       var a, c;
       a = 100; 
       c = function(){
           return b;    // 存在引用,但没有建立闭包。
       }; 
       c();
   }
   console.log( my_func() );  // 函数执行过程结束,变量my_func其值为undefined。
</script>    

<script type = "text/javascript">
    //  例子2
   var b = 20;
   var my_func = function(){
       var a, c;
       a = 100; 
       c = function(){
           return a;    // 返回的是a,不是b,建立了闭包。
       }; 
       c();
   }
   console.log( my_func() );  // 函数执行过程结束,变量my_func其值为undefinded。
</script>  

一旦返回的是内部变量,JavaScript引擎就会在原有的作用域里面插入一个创建好的闭包(保存着内部变量)。该函数运行时,之前的变量值由于在函数作用域内就可以再次被使用了。

综合上述:

JavaScript中的闭包(closure)不是指集合上的关系,而是函数作用域的一部分。