- 最近刚刚接触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的闭包。