闭包与垃圾回收机制的关系
内存管理
对于内存的管理,Javascript与C语言等底层语言JavaScript是在创建变量(对象,字符串等)时自动进行了分配内存,并且在不使用它们时“自动”释放。 释放的过程称为垃圾回收。这个“自动”是混乱的根源,并让JavaScript(和其他高级语言)开发者错误的感觉他们可以不关心内存管理。
内存生命周期
对于计算机语言,内存生命周期基本上都是一样的,大致分为三个部分:
1.分配你所需要的内存
2.使用分配到的内存(读、写)
3.不需要时将其释放\归还
所有语言第二部分都是明确的。第一和第三部分在底层语言中是明确的,但在像JavaScript这些高级语言中,大部分都是隐含的。
javascript的内存分配
1.值的初始化
为了不让程序员费心分配内存,JavaScript 在定义变量时就完成了内存分配。
2.通过函数调用分配内存
有些函数调用结果是分配对象内存
3.有些方法分配新变量或者新对象
使用值
使用值的过程实际上是对分配内存进行读取与写入的操作。读取与写入可能是写入一个变量或者一个对象的属性值,甚至传递函数的参数。
当内存不再需要使用时释放
大多数内存管理的问题都在这个阶段。在这里最艰难的任务是找到“哪些被分配的内存确实已经不再需要了”。它往往要求开发人员来确定在程序中哪一块内存不再需要并且释放它。
高级语言解释器嵌入了“垃圾回收器”,它的主要工作是跟踪内存的分配和使用,以便当分配的内存不再使用时,自动释放它。这只能是一个近似的过程,因为要知道是否仍然需要某块内存是无法判定的(无法通过某种算法解决)。
垃圾回收
垃圾回收算法主要依赖于引用的概念。在内存管理的环境中,一个对象如果有访问另一个对象的权限(隐式或者显式),叫做一个对象引用另一个对象。例如,一个Javascript对象具有对它原型的引用(隐式引用)和对它属性的引用(显式引用)。
在这里,“对象”的概念不仅特指 JavaScript 对象,还包括函数作用域(或者全局词法作用域)。
举个例子:
var o = {
a: {
b: 2,
},
};
//这里有两个对象,一个key 为a 一个key 为b
// 两个对象被创建,一个作为另一个的属性被引用,另一个被分配给变量o
// 很显然,没有一个可以被垃圾收
集
var o2 = o; // O2 引用 key 为a 的对象
o = 1; // o // 这个时候 只有 o2引用 key为a 的对象; o 不在引用了
var oa = o2.a; // key 为 b 的对象 被 oa变量和 a 属性引用
var o2 = o; // O2 引用 key 为a 的对象
o = 1; // o // 这个时候 只有 o2引用 key为a 的对象; o 不在引用了
var oa = o2.a; // key 为 b 的对象 被 oa变量和 a 属性引用
o2 = "yo"; // 虽然最初的对象现在已经是零引用了,可以被垃圾回收了
// 但是它的属性a的对象还在被oa引用,所以还不能回收
oa = null; // a属性的那个对象现在也是零引用 它可以被垃圾回收了
总结:代码从头执行到尾部,如果说这个对象,以及对象中属性;没有被任何全局变量所引用(直接,间接),那么这个对象就会被回收
下面我们来谈谈闭包与垃圾回收机制的关系
我们看一下下面这段代码
var a = 0;
return function () {
console.log(++a);
};
}
// 因为 res 引用的 return 返回的函数
// 在res执行时候;需要 引用foo 的 AO 对象下的 a 属性。
// 所以 foo 中的AO 对象不会被【回收】
var res = foo();
res();
res();
// 当闭包完成任务后,
// 找到完成任务的条件;然后清空res 那么;foo的AO对象就不会再被引用;
// 所以实现了【清空闭包的缓存】
res = null;
// foo 中的 AO 会被回收
foo()(); // 因为没有任何变量和属性;引用return 返回的函数 所以 ao 回收 ao销毁