JS闭包

闭包的定义

展开

MDN中对于JS闭包的解释:

词法环境即为函数定义时所在的作用域,也可以理解为VO。即一个函数,如果可以访问外部作用域的变量,那么该函数和周围环境的组合就是闭包。

从该定义来看,JS中的函数都具有闭包的特性,都可以称之为闭包。但从严格意义来讲,JS中的一个函数,如果访问了外层作用域的变量,那它就是一个闭包。

闭包导致的内存泄露

展开

以如下代码为例:

1
2
3
4
5
6
7
8
9
10
function foo(){
var a = 1;

function bar(){
console.log(a);
}
return bar;
}

var bar = foo();

bar函数与变量a形成了闭包,且通过bar引用了foo函数返回bar函数,在GO中存在bar对foo函数返回bar函数对象的引用,bar函数对象的[[Scopes]]属性又存在对foo函数的AO的引用,根据垃圾回收机制中标记清除算法的处理,bar变量即使以后未被调用,bar函数和foo函数的AO所占据的内存也不会被回收,就造成了内存泄漏。

所以,对于闭包的内存泄漏,其实就是引用链中的对象无法被释放。

解决方法:

手动进行bar = null,手动释放对对象的引用。

常见的垃圾回收机制(GC)算法

展开

垃圾回收机制(Garbage Collection)简称GC。

对于那些不再使用的对象,我们称之为垃圾,它需要被回收以释放更多的内存空间。

在JS中,内存管理是自动的、无形的,就是由于GC的存在帮我们自动管理内存。

常见的GC算法有:

  • 引用计数法

    • 每当存在对象的引用时,相应对象引用计数加1,当引用计数为0时,该对象被回收。
    • 但容易产生循环引用,即两个对象的属性进行相互引用,形成环形链表,导致内存泄漏(两个引用对象的变量赋值为null,但其属性仍然存在引用,导致对象中的引用计数不为0)。
  • 标记清除法

    • 核心思路为可达性
    • 需要设置一个根对象(root object),在浏览器中为window,垃圾回收器会定时从根对象出发,找所有从根对象开始有引用的对象,对于没有引用的对象,就认为是不可用的对象,就会被垃圾回收其处理。
  • 标记整理法

    • 在标记清除法类似,不同的是,在回收的同时会将保留的对象搬运到连续的内存空间中,整合空闲空间,避免碎片化。
  • 分代收集法

  • 增量收集法

    • 将回收分为几部处理,减少垃圾回收的延迟。
  • 闲时收集法

    • 在CPU空闲的时候进行回收。