前言
闭包在JS是一个很难也很重要的一个基础知识点,它的作用其实就是可以允许函数访问并操作其外部的变量,但要追究它的实现过程及应用场景,其中的弯弯绕绕,我在学习是也是痛苦不已,接下来就分享一下我的一些心得。
闭包
在了解闭包之前,有必要先了解一下作用域链,这有利于闭包的理解。
(1)作用域链
首先我们来看这一份代码
function bar (){
console.log(a);
}
function foo(){
var a = 100
bar()
}
var a = 200
foo()
这份代码先是声明了bar()和foo()这两个函数,又声明了一个变量a=200,最后调用了foo()这个函数,
而众所周知,一份代码的预编译总是在执行的前一刻,
那么这里的代码在v8的眼里,我来模拟一下执行流程:
首先是全局中的变量环境中有了函数bar和foo,还有变量a,之后foo被调用;
此时是函数foo因为函数作用域而入栈,开始执行内部的代码,在foo内部又声明一个变量a被赋值为了100,注意此处是在函数foo内又定义了一个变量a与全局的变量a无关!!进行了赋值操作后,最后是函数bar的调用;然后bar的执行上下文入栈,bar的代码很简单,即控制台输出a的值,在输出a的值之后,bar代码执行完毕,执行上下文出栈,然后foo的代码也执行完毕,出栈,最后全局代码执行完毕,执行流程结束。
那么问题就来了,哪个a的值会被找到呢?是全局的a吗?抑或是foo中的a?当时才学到预编译的我来说,有点让我😵了,应该是foo中的a吧?不是在foo中调用的吗,那应该是先拿到foo的a吧!!于是输出100,然后
??让当时的我更😵了,输出了200?也就是说,bar拿到了200?
这就涉及了第一个主题:作用域链。
在一个函数执行前会发生预编译,将执行上下文存入调用栈中,然后会在上下文的变量环境中存入一个非常重要的属性---outer,而这个属性的作用就是指向该函数的外层作用域,而众所周知,内部作用域可以调用到外部作用域的内容,那么这个属性的指向就很重要了,它决定了这个函数可以拿到哪些数据
而outer的指向也很有意思,是根据这个函数的词法作用域规定的,而词法作用域是☞一个域所处的环境,即函数声明的位置。
那么回到这份代码,函数bar声明在了全局作用域,于是找到了处于全局的变量a,输出了200。
总结一下,js引擎在查找变量时会先在函数中查找,找不到就会根据outer的指向去外层作用域里查找,层层往外,这种查找的关系链就称为作用域链
再回过头来,再来理解闭包就容易多了。
闭包
我们再来看这段代码
function foo(){
var name = 'ace'
function bar(){
console.log(count,age);
}
var count = 1
var age = 18
return bar
}
var age = 20
const baz = foo()
baz()
我们先来看下这段代码的大致执行流程:
首先,全局执行上下文入栈,我们有了函数foo(),然后变量age=20,然后变量baz的值被赋为了函数foo的执行结果,那么函数foo的执行上下文入栈,定义了name = 'ace',声明了函数bar,再定义了变量count=1、age=18,最后把bar这个函数返回出去,函数执行结束,foo上下文出栈,baz被赋值为bar()这个函数体,最后调用baz,而baz指向了函数bar,于是然后bar上下文入栈,控制台输出count和age的值后,bar上下文出栈,全局上下文也出栈,代码执行结束。
于是,关键就来到了count和age的值输出什么上了。
按照作用域链的查找,首先是函数bar内部,没有匹配项,来到foo?可此时foo都出栈了,怎么找得到呢?那就来到全局,匹配到了age = 20,再找?没了!!那么就输出undefined,20,对吧?那么
嗯!!!怎么找到了1和18,foo不是都出栈了吗?这里就涉及到今天的主题---闭包了。
闭包指的是当一个内部函数被返回到外部作用域调用时,此时即使函数已经执行完成,即将出栈,也会将内部函数会用到的变量"打包"为一个集合存储在内存中,而这个集合就被称为闭包。
这也就解释了上述代码为何会输出1、18,原来是被打包了,使得bar依旧可以访问到count和age。
那么闭包有啥用呢?
那么就来到了闭包的作用了:
首先,在上述代码中,我们就有了第一条:减少全局变量的创建。这在大型企业级的开发就有大作用了,要是调用函数一直创建全局变量,不说全局变量一多会不会影响其他函数,就是这name有多难起,我想大家也是知道的。
然后,还可以做缓存。即闭包可以保存函数内部的状态,使得函数在多次调用之间可以记住并累积信息,实现函数的记忆功能。
还可以封装模块,等等还有很多,我就不一一列举了
结语
闭包有点弯,有点绕,但静下心来细细品味,还是很简单的,希望我的分享对你有帮助。
乾坤未定,你我皆可上岸。
感谢你的阅读,我们下次再见!!!!