根据我的理解,JS垃圾收集器会删除不再引用的对象。假设我删除了对象的引用,并且该对象具有一个属性,该属性是对另一个对象的引用。这两个对象都会被删除吗?
例如
var objectA = {
prop1: "property",
prop2: "property"
}
var objectB = {
refToA: objectA
}
// Remove reference to objectA
delete objectA;
// Remove reference to objectB
delete objectB
现在这两个对象是否已从内存中完全删除?
答案 0 :(得分:2)
简短回答:是的。无论在任何地方引用的对象都将从内存中完全删除,无论该对象属性引用了什么。
在你的代码片段中,事情相当简单,但是当你开始使用闭包时,内存管理可能会变得棘手:
var objA = (function()
{
var objAb = {who:'An object literal in closure scope',globalReference:'None!'};
return {who:'Return value, is an object literal that references a closure object'.
closureObj : objAb};
})();
var objB = {who:'some object',objAb:{who:'object literal, referenced by objB.objAb'}};
var objBAb = objB.objAb;//reference to obj literal, referenced by objB.objAb
var objAb = objA.closureObj;//reference to obj literal, referenced by objA.closureObj, which in turn references the closure object literal
delete objB.objAb;
console.log(objBAb);//<-- the object literal that was declared as a property of objB still exists
delete objAb;//<-- this reference is gone, but the closure obj still exists
console.log(objA.closureObj);//<-- object is there
基本上,对象是无名实体。用于访问它们的变量是 references , never,ever 包含实际对象本身。它漂浮在JS空间中。当您使用delete someVar
时,您所做的就是取消设置该变量的实际值,即内存地址(排序)。
如果JS GC找不到任何引用内存中包含对象的位置的变量,它将回收该内存。
就这么简单。
将此逻辑应用于以下代码时:
var objA = (function()
{
var closureObj = {iam:'An object literal defined inside a closure scope'};
var functionsAreObjects = function()
{//nameless function object, inside closure scope, too
return closureObj;
};
var resetFunction = function()
{
this.closureReference = functionsAreObjects();//assign return value to this
};
return {iam:'The returned object literal',
closureReference:closureObj,
reset:resetFunction,
getClosureReference:functionsAreObjects};
})();
delete objA.closureReference;//the closure object IS NOT GC'ed
在前面的例子中,最后一个delete语句对于闭包对象文字来说已经足够了。但是,现在objA
有两个方法(引用函数对象的属性)。这些方法仍然引用闭包对象,和仍然被objA
引用,因此closureObj
无法进行GC。所以,这就是事情变得棘手的地方:
delete objA.closureReference;
delete objA.reset;
delete objA.getClosureReference;
我们已经删除了所有属性,这些属性可以链接回closureObj
,因此可以确定它是GC,对吧? - 呃,不太好。 Chrome的V8确实释放了内存,但我听说过类似的代码导致Opera出现泄漏,并且可能IE不会在回收内存方面表现不佳。
此外,我们已经使用getClosureReference
有效地创建了 getter 方法,因此在现实生活中,很可能会发生这种情况:
//do stuff
delete objA.closureReference;
var objB = objA.getClosureReference();//<-- created new reference to closure object
//do some more stuff
delete objA.reset;
delete objA.getClosureReference;
在这种情况下,closureObj
无法进行GC,因为objB
某处仍在引用它。只有当该变量超出范围时才会释放closureObj
。这不仅是一个非常可靠的论据反对全局变量(它们永远不会超出范围,因此永远不会得到GC),它也表明封闭,虽然它们很整洁,但需要一些开发人员的更多开销:让变量超出范围并不一定意味着释放内存:某些闭包可能会暴露对象的引用,或者引用该对象的函数......
我在这个问题a while ago上发布了一个问题,但它可能会解释一些事情。
只是为了好玩,如果你想要一个嵌套闭包的例子(闭包中的闭包,闭包,将引用传递给彼此和其他对象)try finding out when what can be GC'ed in the following code