闭包的概念
闭包跟函数最大的区别在于,当捕捉闭包的时候,它的 自由变量 会在捕捉时被确认,这样即使脱离了捕捉时的上下文,它也能照常运行
闭包: 函数和它可以访问的自由变量
// 函数执行前才创建AO对象
function foo() {
var name = "foo" //自由变量
function bar() {
console.log(name);
}
return bar
}
var fn = foo()
fn()
上面的代码在内存里
- 全局代码编译阶段:创建GEC放入ECS中,vo:GO,foo函数预解析 GO: {foo:引用地址1,fn:undefined}。
- 全局代码执行阶段:foo执行前创建FEC,VO:AO,解析foo内部,AO:{name:undefined, bar: 引用地址2}
- foo执行阶段:AO:{name:foo, bar: 引用地址2},返回出去。 GO:{fn:引用地址2},foo的FEC出栈
- 执行fn(bar函数):打印name变量
name是存在foo的执行上下文,却能脱离了自身的上下文在bar的上下文访问到。所以name是自由变量, 而bar就是闭包。
闭包的内存泄漏
当在第3步,foo的FEC出栈后,原本的{VO:AO}也随之消失,只剩下AO和bar函数互相引用,按理会根据标记清除算法(从根开始查找没有被引用到的对象)被GC回收,但是GO的fn指向了bar函数,所以不会被回收,造成内存泄漏如下图。