前言
要理解闭包,我们需要了解js上下文环境和作用域链的知识,有兴趣的同学可以看看下面这两篇,或许对你有些许帮助:
javascript中神奇的预编译
javascript的作用域与作用域链
概念
闭包就是能够读取其他函数内部变量的函数。
首先我们来了解什么是闭包,总结为一句话就是:函数作为返回值、函数作为参数传递。
第一种:函数为返回值
1 | function test1() { |
第二种:函数为参数
1 | var max = 10 |
原理
我们来使用上面第一个例子来解读
1、当test1函数被定义时,系统生成[[scope]]属性,[[scope]]保存该函数当作用域链,该作用域链的第0位存储当前环境下的全局执行上下文GO,GO里存储的全局下的所有对象,其中包含函数test1和全局变量c
2、当test1被执行时,生成test1函数的AO储存在作用域链的顶端,同时GO被排在第1位,而此时test2被定义,此时test2的作用域链和test1相同
3、当函数test1执行结束时,因为test2被返回到外部,且被全局变量test3接收。这时test1的AO并没有被销毁,只是把线剪断了,因为test2没有执行,只是被返回到外部,并把test1(也是test2)的AO携带出去了,test2的作用域链还连着的。
4、test3执行,即test2开始执行,此时test2拥有自己的作用域链,并且在该作用域链的顶端增加了test2的AO。根据作用域链原理,当执行到console.log(a)时,现在test2的AO里寻找a,此时没有找到,让后去test1的AO里查找,找到了则此时操作的AO为test1的AO。当再次执行test3时,实际操作的仍然是原来test1的AO。
5、当test3执行结束后,test2的AO被销毁,但是原来test1的AO被test2携带到了外部导致无法被销毁,test2的AO依然存在。所以,我们知道为什么说闭包会导致内存泄漏了吧!
注意: 当内部函数被返回到外部并保存时,一定会产生闭包,闭包会产生原来的作用域链不释放,过度的闭包可能会导致内存泄漏,或加载过慢。
夯实基础
例一:
1 | function test1() { |
例二:
1 | function breadMgr(num) { |
例三:经典面试题
1 | function test() { |
实例解读:
1、for循环中添加了10个方法,被储存在数组arr中。
2、这些方法并未被执行,而是被返回到了外部并赋值给了变量myArr,因为未被执行,所以此时匿名函数的AO为函数test()的AO。
3、当执行myArr中当方法时,此时匿名函数生成自己的AO。
4、当程序执行到console.log(i)时,js会自动先去匿名函数的AO中寻找变量i,此时没有找到,然后去test函数中寻找,找到了,而此时i的值在test函数执行完之后变成了10,所以,不论怎么执行myArr的方法,i的值始终为10。
解决办法:
方法一:通过外部力量强行改变
1 | function test() { |
方法二:函数自执行, 通过立即执行函数使i随for循环时改变
1 | function test() { |
- Post title: 理解闭包
- Create time: 2021-03-03 13:15:00
- Post link: 2021/03/03/闭包/
- Copyright notice: All articles in this blog are licensed under BY-NC-SA unless stating additionally.