闭包是什么?用一句话非常经典来概括就是:
在javaScript中,根据词法作用域的规则,内部函数一定能访问外部函数的变量,当内部函数被拿到外部函数之外调用时,即使外部函数执行完毕,但是内部函数对外部函数中的变量依然存在引用,那么这些被引用的变量会以一个集合的方式保存下来,这个集合就是闭包。
单单用语言来描述可能还是有些苍白,那么,再看完这个实例,闭包问题就更不在话下。
闭包是怎么运行的:
现在有这样一段代码,你能判断打印的结果是什么吗?
function foo(){
function bar(){
var age = 18
console.log(myName);
}
var myName = '小明'
return bar
}
var myName = '小红'
var fn = foo()
fn()
答案是小明
,为什么?正常流程下,我想大部分人都会认为答案是小红
,所以这个结果小明
到底是怎么来的?接下来,就让我们一步一步揭开闭包机制的面纱。
首先是预编译阶段,产生全局执行上下文:
要注意此时预编译中的fn
还是处于undefined
状态。
第二步预编译结束,开始执行:
执行到第11行时调用foo
函数,于是产生foo
的执行上下文入栈,等foo
执行完毕后因为返回值为函数,所以全局执行上下文中的fn = undefined
变为fn = function
。
精彩的时候来了,foo函数执行完毕后被垃圾回收机制销毁。
JS中有这样的垃圾回收机制:当foo
执行完毕,通过return bar
返回bar
函数后就会被销毁。那么现在问题来了,既然foo
函数执行上下文被销毁,小明
这个输出结果又是怎么来的?
答案就是闭包。
同样的,代码执行到第12行,bar
函数被调用产生执行上下文,这时候代码要求输出一个myName
,但是bar
自己又没有这个变量,正常流程下,它会顺着作用域链找到他的外部函数foo
,寻找foo
中有没有这个变量。
上文我们提到,foo
的执行上下文执行完被销毁,但是它仍然保留了一个“小箱子”,这个小箱子就是闭包,
可以看到,图中深刻的诠释了当内部函数被拿到外部函数之外调用时,即使外部函数执行完毕,但是内部函数对外部函数中的变量依然存在引用,那么这些被引用的变量会以一个集合的方式保存下来,而这个集合就是闭包,这句话。
因此,通过闭包这个机制,即使foo
执行上下文被销毁,bar
仍然成功的拿到了其中的var myName = '小明'
,于是结果输出为小明
。
最后:
就是这样一个例子却包含了整个闭包的概念,虽然简单,但也足够深刻。