浅谈JavaScript闭包(一)

102 阅读4分钟
  • 最近刚刚接触js,这两天学习了js中比较难搞的一个部分————闭包,在许多前辈眼里,这是js难以逾越的一座大山,而js中另一座大山则是this。下面就让我们跟随着前辈的脚印,一步步翻越这座大山。以下鄙人简单的谈谈对于这座大山(js闭包)的理解,如有错误,请谅解。。。。。。
  • 首先我们要了解一下js中函数的作用域链,在js的世界里,有这么一句话叫做“万物皆可为对象”,其中自然包括了函数,即一个函数也是一个对象,既然是对象,那么它就也能有属性。函数自身存在一个属性scope,即作用域,它是一个隐式的属性。
  • 除此之外,我们还需了解另外一个关于js的执行机制,为了更好的理解,我们直接上代码:
function a() {
           function b() {
               var b = 222    
           }
           var a = 111
           b()
           console.log(a)
   }
var glob = 100
a()
  • 我们来简单探讨一下关于上面这段代码的执行过程,首先,代码在执行之前先进行一个预编译过程,为了便于理解,我们先不去深究这个过程,只是大概走一下它的重要步骤。首先,系统会为我们创建一个全局作用域下的GO对象(我们可以把它理解为在一个画布上画一个大框框),那么这个所谓的GO对象是干吗的呢?框框画好了,当然得往里面加东西,那么加什么呢?且听我细细道来,下一步,它将在全局(即所有代码所在的地方(作用域))寻找形参以及变量声明,看以上代码,形参是没有的,那么变量声明有吗?有的,glob就是一个,紧接着我们把glob这个变量放入GO对象,并赋予undefined(别问为什么,它就是这么干的)。之后,寻找全局下的函数声明,并把函数名作为GO对象的属性名,函数体作为值赋予它。显然function a() 为函数声明,我们将它加入到GO对象,此时全局下的预编译就完成了,结果如下:
    GO {
    	glob:undefiend
    	a:function a() {...}
    }	
    
  • 完成全局的预编译后,下面就是函数体里面的预编译,这时,系统会创建一个AO对象,这个AO对象就相当于在前面提到的大框框中在画一个框框,跟前面一样,我们往里面加东西。下面,依旧是找形参以及变量声明,形参是没有的,变量声明有个a,于是我们将a加入AO对象,并赋值为undefined,最后一步是将形参和实参统一,这里是不需要的。接着在函数体里面找函数声明,显然function b()符合条件,我们将他加入AO对象。
    aAO {
        a: unfefiend
        b: function b() {...}
    }
    
  • 讲道理,接下来就是代码的执行阶段了,但是在a函数执行的时候里面有个b函数,于是它便对b函数进行预编译,同样如上,可得到另一个AO对象(为了避免混淆,我们上面的称为aAO,下面的称为bAO,结果如下:
    bAO {
        b: undefiend
    }
    

-为了便于观看,我们将代码移到下面:

   function a() {
     function b() {
         var b = 222    
     }
     var a = 111
     b()
     console.log(a)
   }
   var glob = 100
   a()
  • 接着执行过程,在全局作用域下,100被赋给了glob,于是GO对象变成了如下:

    GO {
    	glob: undefine -> 100
    	a: function() {...}
    }
    
  • 然后就是函数的执行,先是对b函数进行预编译,我们上面已经完成,然后将111赋予a,接着是b函数执行,将222赋予b,此时aAO和bAO对象就变成了如下:

    aAO {
      a: 111
      b: function b() {...}
    }
    bAO {
      b: 222
    }
    
  • 然后就是剩一个输出语句,此时a的值为111,(变量a在寻找值是是从内往外找的)于是最终执行完这段代码,输出结果为111。这篇文章就行分享到这里,后续我们再进一步剖析js的闭包。