垃圾收集

JavaScript基于引用计数规则自动收集垃圾。若是一个对象再也不被任何一个“引用”引用,那么称此对象不可达。JavaScript垃圾回收机制会在接下来的某一个时刻(没法预知的某时刻)回收此对象。 数组

var name = "hello";
name = name.toUpperCase();
// 此时“hello”对象已没有引用能够到达,因此“hello”对象会在接下来的某时刻被回收

对象之间引用可达性致使不能回收的状况以下所示: 浏览器

/**
 * 主人,具备多个宠物
 * @param {Type}  
 */
function Host() {
    "use strict";
    this.pets = {};
}
/**
 * 收养宠物
 * @param {string} name
 * @param {Pet} pet
 */
Host.prototype.addPet = function (name, pet) {
    "use strict";
    this.pets[name] = pet;
    pet.host = this;
};
/**
 * 由于某些缘由放弃宠物
 * @param {string} name
 */
Host.prototype.removePet = function (name) {
    "use strict";
    this.pets[name].remove();
    delete this.pets[name];
};
/**
 * 宠物
 */
function Pet() {
    "use strict";
    this.host = null;
}
/**
 * 宠物忘记本身的主人
 */
Pet.prototype.remove = function () {
    "use strict";
    this.host = null;
};
/**
 * 狗
 */
function Dog() {
    "use strict";
}
Dog.prototype = new Pet();
/**
 * 狗不会忘记本身的主人
 */
Dog.prototype.remove = function () {
    "use strict";
};
/**
 * 猫
 */
function Cat() {
    "use strict";
}
Cat.prototype = new Pet();
如今让Host收集一只狗与一只猫

var host = new Host();
var dog = new Dog();
// 收养宠物狗
host.addPet("xiaoGou", dog);
var cat = new Cat();
// 收养宠物猫
host.addPet("xiaoMao", cat);

因此可知当前这个三对象在内存中的状况以下所示: 闭包

0b2aa85471f1f4167743262a297acd8b

从上图能够看出,在内存中Host对象的pets数组中的元素分别指向Dog与Cat对象,而Dog与Cat对象中的host属性都指向Host对象。因此当前三个对象均可以经过某个引用到达,因此此三个对象都不会被回收。 app

若是某一个主人由于某此缘由不能再收养宠物时 函数

// 放弃宠物狗
host.removePet("xiaoGou");
// 放弃宠物猫
host.removePet("xiaoMao");
host = null;
cat = null;

Host使用removePet方法来放弃宠物,并在此方法中调用了pet.remove方法,使宠物也忘记本身的主人。可是因为Dog对象重写了remove方法,因此Dog并无忘记本身的主人,因此当前的内存状况以下所示: this

8613d779581a4900b5fc1dfa51040c0c

中于没有把dog引用设置为null,因此经过dog引用还能够到达Dog对象,而又由于Dog对象的host属性还指向Host对象,因此Host对象也是可达的,因此Dog与Host对象都不能被回收。最后把dog引用也设置为null,这时由于Dog对象不可达,因此Dog与Host对象都会被回收。spa

dog = null;

因而可知对于“可达性”的判断是指某一个对象是否能从JavaScript执行环境中的某引用出发而引用到此对象。 prototype

DOM节点与JavaScript对象之间引用致使不可回收的状况。 3d

function createElem() {
    "use strict";
    var elem = document.createElement("div"); // 动态建立一个Div
    elem.id = "div_id";
    document.body.appendChild(elem); // 把Div添加doby中
}
createElem();

以上代码向body中动态建立了一个Div,并把Div添加到了DOM树中显示。尽管createElem方法执行完成以后,它做用域内的变量都已不可达,可是由于Div已被添加到了DOM树中,因此Div还存于内存中没有被回收。一至到Div被从DOM树中的删除,那么Div节点才会被回收。 code

function deleteElem(id){
    var elem = document.getElementById(id);
    document.body.removeChild(elem);
}
deleteElem("div_id");

DOM节点也可能与JavaScript对象之间造成循环引用致使不可回收的状况。

function Button(text) {
    var button = document.createElement('input');
    button.type = "button";
    button.value = text;
    this.className = "Button";
    this.button = button; // Button对象的button属性指向input[type="button"]的DOM节点。
    button.self = this; // input[type="button"]的DOM节点的self属性指向了Button对象。
}
// 添加Button对象到某个位置
Button.prototype.appendTo = function (parentElem) {
    parentElem.appendChild(this.button);
}
// 添加Button的单击事件
Button.prototype.addClickEvent = function(func) {
    this.button.onclick = func;
}
// 移除
Button.prototype.remove = function(parentElem) {
    parentElem.removeChild(this.button);
}

使用以上代码建立一个Button对象。

// 建立Button对象
var btn = new Button("show className");
// 为Button添加一个单击事件
btn.addClickEvent(function () {
    console.log( this.self.className ); // Button
});
var parentElem = document.getElementById("parent_id");
// 把button添加到DOM树中
btn.appendTo( parentElem );

在建立了一个Button对象以后,再给它添加了一个单击事件,并把它添加到DOM树中。当前在内存中造成的状况以下图所示:

ae95e3561e966b5ab4fa8fbefd7a02bc

如今若是input[type="button"]已使用完成,把它从DOM树中删除。

btn.remove( parentElem );
btn = null;
parentElem = null;

以执行以上代码以后,btn到Button对象的引用已不可达。input[type="button"]节点也从DOM树中删除。可是Button对象与input[type="button"]节点还不能被回收。由于DOM节点与JavaScript对象处于浏览器的不一样引擎中(DOM节点处于渲染引擎,JavaScript对象处于JavaScript引擎),它们之间的相互引用就造成了循环引用,因此此时的Button对象与input[type="button"]节点都不能回收。具体的内存状况以下所示:

dd905fd0a05ccc52f38302034b322ad1

因此要想回收Button对象与input[type="button"]节点,就要断开它们之间的引用。修改代码以下所示:

// 移除
Button.prototype.remove = function(parentElem) {
    this.button.self = null; // 断开input[type="button"]到Button对象的引用
    parentElem.removeChild(this.button);
    this.button = null; // 断开Button对象到input[type="button"]的引用
}

再调用remove方法,把input[type="button"]节点从DOM树中移除。

btn.remove(parentElem);
btn = null;
parentElem = null;

以后,内存的状况以下所示:

30bbd8628faa9da41f6c7f468c0a9b69

能够从上图看出Button对象与input[type="button"]节点之间再也不有引用关系,因此这时Button对象与input[type="button"]节点均可以被回收。

还可能由闭包引发的内存不能回收的状况。

function outter() {
    "use strict";
    var obj = {
        name : "wanggang",
        age : 100
    };
    return function () {
        return obj;
    };
}

var func = outter();
var obj = func();
console.log(obj.name);
obj.name = "yxf";

当执行12行的outter方法时,它会返回函数。而这个函数再会引用着outter函数的中一个局部变量,因此当没有执行13行代码以前,虽然outter函数的生命周期已结束,可是outter的obj变量还存在于内存中,没有回收。只有当执行完13行的代码以后,outter函数中的obj变量才会被回收。

(本文的部分例子来自于Ajax in Action)

相关文章
相关标签/搜索