什么是JS闭包?为何会造成内存泄漏?如何解决?

80 阅读1分钟

闭包的概念

闭包跟函数最大的区别在于,当捕捉闭包的时候,它的 自由变量 会在捕捉时被确认,这样即使脱离了捕捉时的上下文,它也能照常运行

闭包: 函数和它可以访问的自由变量

// 函数执行前才创建AO对象
function foo() {
  var name = "foo" //自由变量
  function bar() {
    console.log(name);
  }
  return bar
}
var fn = foo()
fn()

上面的代码在内存里

  1. 全局代码编译阶段:创建GEC放入ECS中,vo:GO,foo函数预解析 GO: {foo:引用地址1,fn:undefined}。
  2. 全局代码执行阶段:foo执行前创建FEC,VO:AO,解析foo内部,AO:{name:undefined, bar: 引用地址2}
  3. foo执行阶段:AO:{name:foo, bar: 引用地址2},返回出去。 GO:{fn:引用地址2},foo的FEC出栈
  4. 执行fn(bar函数):打印name变量

name是存在foo的执行上下文,却能脱离了自身的上下文在bar的上下文访问到。所以name是自由变量, 而bar就是闭包。

闭包的内存泄漏

当在第3步,foo的FEC出栈后,原本的{VO:AO}也随之消失,只剩下AO和bar函数互相引用,按理会根据标记清除算法(从根开始查找没有被引用到的对象)被GC回收,但是GO的fn指向了bar函数,所以不会被回收,造成内存泄漏如下图。

closure.png

解决:fn=null