闭包这块,面试官都没你讲得好

5,680 阅读3分钟

闭包是什么?用一句话非常经典来概括就是:

在javaScript中,根据词法作用域的规则,内部函数一定能访问外部函数的变量,当内部函数被拿到外部函数之外调用时,即使外部函数执行完毕,但是内部函数对外部函数中的变量依然存在引用,那么这些被引用的变量会以一个集合的方式保存下来,这个集合就是闭包。

单单用语言来描述可能还是有些苍白,那么,再看完这个实例,闭包问题就更不在话下。

闭包是怎么运行的:

现在有这样一段代码,你能判断打印的结果是什么吗?

function foo(){

    function bar(){
        var age = 18
        console.log(myName);
    }
    var myName = '小明'
    return bar
}
var myName = '小红'
var fn = foo()
fn()

答案是小明,为什么?正常流程下,我想大部分人都会认为答案是小红,所以这个结果小明到底是怎么来的?接下来,就让我们一步一步揭开闭包机制的面纱。

首先是预编译阶段,产生全局执行上下文:

1722071405263.png

要注意此时预编译中的fn还是处于undefined状态。

第二步预编译结束,开始执行:

执行到第11行时调用foo函数,于是产生foo的执行上下文入栈,等foo执行完毕后因为返回值为函数,所以全局执行上下文中的fn = undefined变为fn = function

1722071339355.png

精彩的时候来了,foo函数执行完毕后被垃圾回收机制销毁。

1722071300789.png

JS中有这样的垃圾回收机制:当foo执行完毕,通过return bar返回bar函数后就会被销毁。那么现在问题来了,既然foo函数执行上下文被销毁,小明这个输出结果又是怎么来的?

答案就是闭包。

同样的,代码执行到第12行,bar函数被调用产生执行上下文,这时候代码要求输出一个myName,但是bar自己又没有这个变量,正常流程下,它会顺着作用域链找到他的外部函数foo,寻找foo中有没有这个变量。

上文我们提到,foo的执行上下文执行完被销毁,但是它仍然保留了一个“小箱子”,这个小箱子就是闭包

1722071190233.png

可以看到,图中深刻的诠释了当内部函数被拿到外部函数之外调用时,即使外部函数执行完毕,但是内部函数对外部函数中的变量依然存在引用,那么这些被引用的变量会以一个集合的方式保存下来,而这个集合就是闭包,这句话。

因此,通过闭包这个机制,即使foo执行上下文被销毁,bar仍然成功的拿到了其中的var myName = '小明',于是结果输出为小明

最后:

就是这样一个例子却包含了整个闭包的概念,虽然简单,但也足够深刻。

b8af76354950f1b7df68cdc7cb1a7d60.jpeg