释放JavaScript对象

25

我正在查看来自http://www.javascriptkit.com/javatutors/oopjs.shtml的示例。

var person = new Object()
person.name = "Tim Scarfe"
person.height = "6Ft"

但是没有提到如何"释放"它以避免内存泄漏。

以下代码能否使其被"释放"?

person = null;
  1. 如何释放使用"new Object()"创建的JavaScript对象?
  2. 如何释放使用"new Array(10)"创建的JavaScript数组?
  3. 如何释放使用"var json = {"width": 480, "height": 640}"创建的JavaScript JSON对象?

感谢您提前的帮助。


2
这不是JavaScript JSON,只是对象字面量表示法。只有当它是一个字符串时才是JSON。另请参阅:http://benalman.com/news/2010/03/theres-no-such-thing-as-a-json/#disqus_thread - Felix Kling
1
离题:你从那个网站获得的示例代码并不是很好。1. new Object() => {}; 2. 它依赖于自动分号插入,如果不是恶魔的产物,仍然是一件你绝对不能依赖的事情;3. 好吧,这一点还不错,但是,为什么不教直接量表示法呢?var person = {name: "Tim Scarfe", height: "6Ft"}; 但实际上,它是#2,稍微有点#1,让我想到了“blech”。 - T.J. Crowder
2个回答

60
您不必显式地“释放”JavaScript对象。所有标准的JavaScript主机/环境都使用基于垃圾收集的内存管理,根据对象是否可以再次访问。 (可能会有一些专门用于嵌入式系统等领域的主机不这样做; 如果是这样,它们将提供自己的手段来显式释放内容。)如果不能再次访问该对象,则可以回收其内存。
您可以确保没有任何东西正在引用您不再使用的内存,因为正在引用的内存无法释放。几乎所有时间都会自动发生。例如:
function foo() {
   var a = [1, 2, 3, 4, 5, 6];

   // Do something
}

一旦foo返回,指向数组a的内存就可以被回收,因为它不再被任何东西引用(a已经超出了范围,没有任何未解除引用的东西)。

相比之下:

function foo() {
   var a = [1, 2, 3, 4, 5, 6];

   document.getElementById("foo").addEventListener("click", function() {
       alert("a.length is " + a.length);
   });
}

现在,由于闭包(事件处理函数)具有对其的活动引用,并且有一些东西使闭包保持在内存中(DOM元素),因此a指向的内存无法被回收。
你可能认为这仅适用于上面的情况,其中闭包明显使用a,但是在这里不使用a似乎并不重要。
function foo() {
   var a = [1, 2, 3, 4, 5, 6];

   document.getElementById("foo").addEventListener("click", function() {
       alert("You clicked foo!");
   });
}

但是,根据规范,即使闭包没有使用它,a 也会被保留,闭包仍然对它有间接引用。(更多信息请参见我的[相当旧的]博客文章Closures Are Not Complicated。)有时候 JavaScript 引擎可以优化掉 a,尽管早期的积极努力被回滚了 - 至少在 V8 中如此 - 因为执行这项分析所需的性能比保留数组在内存中的性能更加影响。

如果我知道该数组不会被闭包使用,我可以通过赋予 a 不同的值来确保该数组不被引用:

function foo() {
   var a = [1, 2, 3, 4, 5, 6];

   document.getElementById("foo").addEventListener("click", function() {
       alert("You clicked foo!");
   });

   a = undefined; // <===============
}

现在,尽管变量a仍然存在,但它不再引用数组,因此可以回收数组的内存。

更多信息请参见StackOverflow上的另一个答案


更新: 我可能应该提到delete,尽管它不适用于你问题中的精确代码。

如果你习惯了其他语言,你可能会认为"啊,deletenew的对应物",但实际上两者完全没有任何关系。

delete用于从对象中删除属性。它不适用于你的代码示例,因为你不能删除var。但这并不意味着它与你可能遇到的其他代码无关。

让我们考虑两个看起来基本相同的代码片段:

var a = {};         // {} is the same as new Object()
a.prop = "foo";     // Now `a` has a property called `prop`, with the value "foo"
a.prop = undefined; // Now `a` has a property called `prop`, with the value `undefined`

对比。

var b = {};         // Another blank object
b.prop = "foo";     // Now `b` has a property called `prop`, with the value "foo"
delete b.prop;      // Now `b` has *NO* property called `prop`, at all

这两种方法都会使指向prop的内存变得可回收,但它们之间有一个区别:在第一个例子中,我们没有移除属性,而是将其值设置为undefined。在第二个例子中,我们完全从对象中删除了该属性。这不是没有区别的区别:

alert("prop" in a); // "true"
alert("prop" in b); // "false"

但是这个概念也适用于你的问题,因为删除一个属性意味着该属性指向的任何内存都可以被回收利用。那么为什么delete不适用于你的代码呢?因为你的person是:
var person;

使用var声明的变量是对象的属性,但它们不能被删除。("它们是对象的属性?"你会问。是的。如果你在全局作用域有一个var,它就成为全局对象[在浏览器中是window]的属性。如果你在函数作用域中有一个var,它就成为一个不可见但非常真实的对象的属性,该对象称为“变量对象”,用于对该函数的调用。无论哪种方式,你都不能删除它们。有关此问题的更多信息,请参见上面的链接关于闭包的内容。)

+1 指出了作用域链和隐藏的“绑定”,不知道是否有任何 JS 引擎足够聪明,能够自动消除这些情况 - 只有在嵌套作用域中使用“eval”或显式使用才能访问绑定(从代码中)。 - user166390
谢谢您的帮助。 "a = null;" 和 "a = undefined;" 会产生相同的效果吗? - pion
3
@aion: "a = null;" 和 "a = undefined;" 会产生相同的结果吗?为了释放所有与 a 相关的内存,是的。我使用 undefined 是因为当属性(和变量)未初始化时,其值就是 undefined,所以我认为 undefined(而不是 null)是属性的“空白”状态。但使用 null 也可以,而且你经常看到它被使用。 :-) - T.J. Crowder
@pst:“我想知道是否有任何JS引擎可以自动消除这些情况。” 如果V8尚未对没有引用或其中没有eval的代码(或更好的,使用严格模式的代码)执行此操作,我敢打赌它最终会做到。或者如果它没有这样做,那么你可以肯定我们没有看到一个非常好的原因。 :-) - T.J. Crowder

1

JavaScript会自动处理这一切,但如果你想显式地释放一个对象或变量,那么将其设置为null是最接近实现此目的的方法。


1
@Phrogz,你不能删除变量,只能删除属性。尝试在页面脚本中运行此代码(而不是在Firebug/WebKit控制台中,因为它会误导你):var a='test',b={c:'test'};console.log('a:',a);console.log('delete a:',delete a);console.log('a:', a);console.log('b.c:',b.c);console.log('delete b.c:',delete b.c);console.log('b.c:',b.c); 然后阅读:http://perfectionkills.com/understanding-delete/ - eyelidlessness
1
哈哈。我本来以为你不能删除变量,然后我看到了其他答案提出的建议,我很懒,只在控制台中测试了一下。真尴尬。:) 如果计时器没有结束,我会撤回我的谎言,所以现在我只能删掉这条评论。感谢你对抗错误信息的警觉性。 - Phrogz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接