注册
web

用剥蒜的方式去学习闭包,原来这么简单!!!

对于很多学习js的码友们来说,闭包无疑是最难啃的模块之一,今天我们就用剥蒜的方式一层一层的剥开它。


我们先用一个案例来引入它



大多数肯定会觉得它输出的结果是0到9,那么大多数人都错了,它输出的结果是十个10
那么要怎么让它输出0到9呢?这里我们要先引入一个新的东西,叫调用栈


调用栈


调用栈是用来管理函数调用关系的一种数据结构


在代码预编译的时候就会产生一个生成一个调用栈,它会先去找到代码中的全局变量、函数声明,形成一个变量环境,这个变量环境和词法环境(这里我们不去探讨)一起放在全局执行上下文中。然后再从上到下去执行代码,每调用一个函数,就会生成一个该函数的执行上下文,然后入栈,函数执行的时候先去它的词法环境去找对应的变量名,找不到再去变量环境中找,再找不到就去它的下一级去寻找直到找到为止,然后函数执行完成后再将其执行上下文销毁,避免栈溢出,我们用一个代码来举例:


iwEcAqNwbmcDAQTRB4AF0QQ4BrAXVDtt0dImQAU9UGjZw-8AB9IZAt15CAAJomltCgAL0gAC84A.png_720x720q90.jpg


执行函数add时,先去add的执行上下文中寻找b,b在add的变量环境中,但是并没有a,于是再去全局执行上下文中按照词法环境和变量环境的顺序去找,找到了a,最终返回a+b=12。


作用域链


调用栈在生成执行上下文时会默认在变量环境中产生一个outer,它指向该函数的外层作用域,函数声明在哪里,哪里就是函数的外层作用域,然后形成一个作用域链。


我们再来看下一个案例



调用foo的时候生成了foo的执行上下文,foo的函数体中有bar的调用,所以又生成了一个bar的执行上下文,bar声明在最外面,所以它的outer指向全局执行上下文,因此当bar在寻找myName这个变量的时候直接跳过foo去了全局执行上下文,所以最终输出的结果是万总


iwEcAqNwbmcDAQTRB4AF0QQ4BrDMeEmBKzv5FAU9V3BEkJoAB9IZAt15CAAJomltCgAL0gADbJY.png_720x720q90.jpg


闭包


了解完调用栈和作用域链之后,就可以进入我们今天的主题闭包了,还是用一个案例来说明



函数a的函数体中声明了一个函数b,并且函数a的结果是返回了函数b


var c = a() 先调用a,并且把a的返回值赋给c,因此c就是一个函数,然后再调用c,这就是整个的执行过程。在调用完a后,a的函数体已经全部执行完毕,应该被销毁,但是在调用c的时候(c就是函数b),需要用到a中的变量,因此在销毁掉a的执行上下文的同时会分出一个区域用来存储b中所需要用到a的变量,这个存储了count的地方就叫做闭包。


iwEcAqNwbmcDAQTRB4AF0QQ4BrCKgRsdcT9JIgU9Y1plkyAAB9IZAt15CAAJomltCgAL0gACzhQ.png_720x720q90.jpg


因此闭包的概念就是:


即使外部函数已经执行完毕,但是内部函数引用了外部函数中的变量依然会保存在内存中,我们把这些变量的集合,叫做闭包


现在我们再回到第一个问题,如何让它输出0到9,很显然,就是在for的内部形成一个闭包,让i每次可以叠加存在内存中,因此代码如下:



这样一层一层把从外到内的去了解闭包,是不是就更容易了呢,你学会了吗?


作者:欣之所向
来源:juejin.cn/post/7300063572074201125

0 个评论

要回复文章请先登录注册