3分钟深入理解JavaScript底层:闭包

134 阅读3分钟

前言

上一篇我们讲了JavaScript中的执行上下文,在调用栈用有一个函数执行上下文和全局执行上下文,当需要的变量在函数执行上下文中没有找到时,引擎便会进入全局执行上下文中查找。可是为什么它会进入全局中查找呢,并且在此之中有什么其他特殊情况呢,这就是我们今天要讲的作用域链与闭包。

作用域链

词法作用域

首先我们引入一个概念叫词法作用域,词法作用域就是一个变量或函数声明的位置在哪一个作用域中,哪一个作用域就是他们的词法作用域。

outer

在引入闭包概念前我们需要先了解作用域链是什么,我们知道在预编译时会产生执行上下文对象,而执行上下文对象中分别存有变量环境和词法环境,其实变量环境中不止有var,还有一个JS语言内定的outer属性,它是用于指明该函数的外层作用域(外层执行上下文)是谁,而outer的指向就是变量的查找方向,其指向是根据词法作用域来决定的,outer会指向当前执行上下文词法作用域的外层作用域。

小结一下,JS引擎在查找变量时,会先在函数作用域中查找,找不到就会根据outer的指向去往到外层作用域中查找,层层向上,这种查找的关系链(规则),就称为作用域链

闭包

首先我们来看一串代码

function sun(){
    var a = 1
    function fk(){
        console.log(a)
    }
    return fk
}
var b = sun()  //var b = funcation fk(){}
b()

首先我们来分析,在全局作用域中定义了一个变量b,定义了一个函数sun(),然后预编译结束,代码开始执行,当b调用sun函数时,创建一个函数执行上下文定义a变量和fk()函数,然后开始执行函数,当return fk执行完毕以后,sun函数执行完毕,在这时它会销毁出栈,那这时我们在外部再调用函数b(),也就是函数fk()时那是不是就什么都不会输出了呢?

image.png 诶,它还是输出了1,可是我们不是说sun()函数应该销毁出栈了吗,应该连同变量a一起被带走了呀,怎么还会输出1呢。没错,sun()函数确实被销毁出栈了,可是变量a却被保留了下来并且在原来作用域的位置形成了一个闭包,接下来我们慢慢解释。

根据我们JS词法作用域的规则,内部的函数一定能访问外部函数中的变量,但是函数使用完又会出栈销毁,这两个规则是不是有些冲突了呢?所以,便产生了一个垃圾回收机制。通常情况下,当一个函数执行完毕后,其局部变量会被垃圾回收器回收。然而,当通过调用一个外部函数,函数返回一个内部函数后,即使外部函数执行已经结束,但内部函数引用了外部函数的变量,这些变量就不能被立即回收,也依旧需要将变量保存,我们把这些变量的集合叫做闭包。

小结

闭包是JavaScript中一个强大且重要的特性,它使得内部函数可以访问外部函数的变量,即使外部函数已经执行完毕。理解并掌握闭包可以用于实现共有变量、持久化状态即做缓存和封装模块,防止全局变量污染中。欢迎在评论区沟通交流,如有不对感谢指出!