边理解边写的,写的很烂,作用域链和闭包的区别都没讲清楚(改天有空了再改改)
定义
- 闭包也叫词法闭包或者是函数闭包;
- 是在支持头等函数的编程语言中,实现的词法绑定的一种技术(补充:词法作用域就是在写代码的时候就已经决定了变量的作用域,与之相对的是动态作用域);
- 闭包在实现上是一个结构体,它存储了一个函数和一个关联的环境
- 闭包和函数最大的区别是,当捕捉到闭包时,它的自由变量(内部函数访问到的外部变量)会在捕捉时被确定,这样即使脱离了上下文,它也能照常运行。
广义上来说,js中的函数都是闭包
狭义上来说,js中一个函数访问外层作用域的变量那么就是一个闭包
闭包内存图
内存图之代码解析过程
在执行上下文栈执行代码之前,先对代码进行解析
创建对象,并且将全局定义的变量和函数关联到GO里面,这里没有画scope作用域链,因为是全局代码,只有全局作用域。
内存图之代码执行
函数createAdder执行
- 创建AO对象,解析函数里的变量,count赋值为5,return adder,拿到即将赋值给adder5的adder地址
- 创建函数执行上下文,并且将它压入执行上下文栈
- 确定scope对象0x031,在这个对象里保存了此函数可访问到的作用域地址(即AO地址)
- 确定作用域的this指向(没画)
函数adder执行
- 将createadder的执行上下文出栈
- 执行全局代码,var adder5=0x02
- 执行adder5(10)
- 创建AO对象,解析AO对象里的变量,将num赋值为10,通过闭包找到count的值
- 确定scope,确定this指向 createadder在执行完后,不释放相关内存,满足了内存管理的可达性。
!!!在上面的代码中使用了闭包,但是并没有讲清楚什么是闭包
在网上查了很多资料,理解了很多文章,有人认为闭包就是函数可以访问到外部作用域的变量,此函数和外部作用域一起组成闭包,还有人觉得闭包是函数里面嵌套函数,并且嵌套函数可以访问到外部函数的变量,这就是闭包.....还有很多其他的观点,上面两个是我学习过程中看到的也是把我搞混淆的观点,于是我买了套《你不知道的javascript》,书里的观点比较清晰明确,所以有人看到我这篇文章,并且刚好也对这些东西有疑问的话,我推荐你去买本书看,比看网上乱七八糟的文章要好,比如我这篇,谁要是看了肯定懵逼。
zhuanlan.zhihu.com/p/22486908 就是看了这篇文章里和评论区的观点,促使我买这本书的,网上的东西太多太杂了,还是看书更好理解。
重新理解闭包
在我上面的例子中,确实使用到了闭包,但是我在对他们进行解释时并没有体现出闭包的特点,而是用作用域链的方式来解释的,实际上作用域链是闭包的一种规则,但只是闭包的一部分而已。
function createAdder(count){
function adder(num){
return num+count
}
adder(10)
}
createAdder(5)
我们来看这段代码,函数createAdder()里面嵌套了一个函数adder(),并且在adder函数里也访问了外部作用域的变量。
通过调试看到,在浏览器里,认为adder函数和它的外部作用域是闭包,但是在《你不知道的javascript》里认为,“在技术上,这也许是闭包,但是确切的说,这并不是,最准确的解释是,adder()对count的引用的方法是词法作用域的查找规则(也就是作用域链查找),而这些规则只是闭包的一部分”。 下面再来看一段代码
function createAdder(count){
function adder(num){
return num+count
}
return adder
}
var adder5 =createAdder(5)
adder5(10)//15
浏览器调试结果,认为adder5函数和它的外部作用域是闭包。
在这段代码里,函数作为返回值被return出去,并且在外部词法作用域里调用了这个函数,上面那段代码adder的调用是在createAdder里面的,当createAdder执行完毕后,createAdder作用域就会被销毁,而这段代码里,createAdder执行完毕后,依旧没被销毁,因为adder5函数在外部作用域执行,而adder5拥有涵盖createAdder作用域的闭包,所以createAdder执行完毕后,并没有被销毁,它的生命周期被延迟的长了,以供adder5在之后运行时引用,这个引用就叫闭包,像这样的闭包是明确且容易观察的。
再看一段代码:
function wait(message){
setTimeout(function timer (){
console.log(message);
},1000)
}
wait('hello,closure')
这段代码里,定义了一个函数wait,内部有一个定时器,时间间隔为1s,timer引用了wait的参数,在wait函数执行完1s后,wait函数作用域并没有消失,因为timer回调仍然拥有对wait作用域的闭包。
我对《你不知道的javascript》中定义的理解,闭包是基于词法作用域书写代码时产生的自然结果,闭包不需要刻意创建,可以根据自己的意愿来识别闭包,内部函数能够延长外部作用域的生存周期,这个函数及封闭这个函数的外部环境就是闭包