JavaScript中的new关键字和内存管理

24

作为一个C++开发者,我会下意识地在每次调用new时也调用delete。但在JavaScript中,尽管偶尔会使用new,我还是希望(希望)浏览器的垃圾回收功能能够自动处理这些。

我不太喜欢这种方式 - 在JavaScript中是否有delete方法,并且与C++中的使用方式有何不同?

谢谢。


22
放手吧......松开你的手指,让那个记忆离去......深呼吸...... - Michael Myers
这里有一个相关的问题,讨论了Javascript的垃圾回收机制。https://dev59.com/Z3RA5IYBdhLWcg3wsgBP - kemiller2002
这个帖子的名字真是讽刺啊... - Dested
2
不要过度占用内存。 - kennytm
11个回答

25
var component = new Component();
component = null; // delete this at next garbage collection

2
对于不习惯垃圾回收(GC)的人来说,这与“删除”相反。对他们来说,似乎意图是永久保留已分配的内存,所以我想它并没有提供问题提出者所寻求的“舒适感”。然而,如果他只是想要一种表达对象不再需要(为了可读性),那么这就成功了。 - G-Wiz
2
如果没有分配给变量,这个怎么工作呢?例如:new Component().init(); 这会有什么性能后果吗? - Danimt

14

其实,在JavaScript中,“new”关键字并不是必需的,也与分配内存无关。所有“new”所做的就是将一个名为“this”的新对象(this = {})作为隐藏参数传递给该函数。

var MyClass = function(){
    // fresh empty object "this" gets passed to function
    // when you use the "new" keyword 
    this.method = function(){}
}

var myInstance = new MyClass();

嵌套闭包和多个“this”变量在不同的作用域中流动,使Javascript变得有点复杂。我更喜欢这种方式:

var MyNoNewClass = function(){
    // I find this more explicit and less confusing
    var self = {}
    self.method = function(){}
    return self;
}

var myNoNewInstance = MyNoNewClass()

删除操作并不直接释放内存,它只是将未定义的值赋给对象的指定属性。 - Bob
@Alsciende 是的,你说得对,谢谢。不过,delete 在 JavaScript 中并不像其他语言一样直接释放内存,它只是从对象中删除属性。 - Bob

5

所有的JavaScript内存都是以参考方式进行的,但不是传统意义上的参考。内存不是通过内存地址引用,而是通过字符串引用。在这段代码中:

var x = new someObj();

从那时起,该对象由字符串“x”引用。此时,x不是指向堆上某个内存的指针。如果您给x分配了一个属性,则:

x.someProp = 42;

那么,someProp是一个字符串,它在内存中引用值42。因此,您可以使用数组表示法通过其字符串表示访问它:

x["someProp"]++;

这就是为什么变量可以持有任何值,因为它们不需要大小。在JavaScript中,内存的收集实际上是在没有更多字符串(也就是变量或属性名称)引用它时进行的。当x被分配为任何其他值时,该对象将被收集。您可以将其设置为null、undefined或其他任何内容,那么该内存将被收集。也就是说,当浏览器或任何JavaScript引擎处理它时,它将被收集。只有delete从对象中删除属性。此后,尝试访问该属性将返回undefined。在大多数情况下,以下两行代码是等效的。
x["someProp"] = undefined;
delete x.someProp;

编辑:好的,实际上这两行代码在内部并不相同。delete操作符会从内存中删除“someProp”引用,而将其设置为undefined则不会。我想是这样的。我在规范中找不到有关将变量或属性设置为undefined的任何内容,但我认为这样做并没有什么特殊的作用。

需要注意的重要事项是,您将无法删除具有某个标志设置的属性,但是您可以将它们设置为null或undefined(如果它们未被setter包装,甚至允许这种情况发生)。


最后关于删除的部分是错误的。赋值未定义和使用删除是两个不同的事情。 - Alsciende
@Alsciende 严格来说也许是这样,但就所有目的而言,它们实现了相同的功能。不过,我可以看出我写的在严格意义上是错误的,所以我会进行编辑,谢谢。 - Bob

3

在浏览器之外,JavaScript 可以通过垃圾回收完全恢复内存。但是,在实际应用中,与 DOM 模型结合使用的垃圾回收可能会导致内存泄漏。

这里有一篇文章http://www.ibm.com/developerworks/web/library/wa-memleak/,你可以在其中找到更多详细信息。


2
为了避免内存泄漏:
  1. 确保删除所有事件处理程序,例如添加鼠标按下事件处理程序后,请在完成后将其删除
  2. 如果向DOM中添加元素,请在完成后将其删除
  3. 如果有一个对象指向另一个对象,请在完成后移除该引用。
  4. 当您完成任何对象时,请将其设置为null。

"delete"仅会从对象中删除属性。


1

new 从构造函数创建对象。delete 从对象中删除属性。这是两个非常不同的事情。

您不必删除已创建的对象。垃圾回收器会处理此问题。

delete o.fuo.fu = undefined 不同。试试这个:

var o = {};
alert(o.hasOwnProperty('fu')); // -> false
o.fu = undefined;
alert(o.hasOwnProperty('fu')); // -> true
delete o.fu;
alert(o.hasOwnProperty('fu')); // -> false

1

在JavaScript中有一个delete。然而,实际需要它的情况很少。它不像C++的delete。

delete会从对象中删除一个属性。随后访问该属性将返回undefined。我认为它能够在随后的垃圾回收中释放任何相关的内存。


1

在 JavaScript 中,除非你要删除事件处理程序,否则不要使用 delete。即使是这样,我们也只是因为旧版本的 IE 存在与此实践相关的内存泄漏问题。Douglas Crockford 很好地解释了 这一点。在他的情况下,他甚至不使用 delete。他只是将相关的值设置为 null


1

来自MDC

delete 运算符可以删除对象的属性或数组中指定索引处的元素。

在我看来,delete 运算符仅在您想要从对象中删除属性时才有用。但是,由于可能存在对其引用的其他引用,因此它不会真正被垃圾回收。如果您希望某些内容被垃圾回收,您需要删除所有指向它的指针,并释放包含对其引用的闭包(有关该主题的更多信息)。


0

"new" 关键字与内存无关,它的作用仅在于设置原型链。

// C++
Foo* foo = new Foo();                      // allocate and construct

// JS "sugar" using the new keyword
var foo = new Foo();                       // allocate and construct
assert(foo instanceof Foo);

// JS constructing objects without "new"
var foo = Object.create(Foo.prototype);    // allocate with prototype
Foo.call(foo);                             // construct
assert(foo instanceof Foo);

// construct Foo with missing prototype
var foo = {};                              // allocate without prototype
Foo.call(foo);                             // construct without prototype
assert(!(foo instanceof Foo));

// so the corollary operations in JS are
foo = {};
foo = null;

// where-as this is an error (or most certainly not what you want)
delete foo;

// and this is how delete is used
foo = {bar: 42};
assert(foo.bar === 42);
delete foo.bar;
assert(foo.bar === undefined);

// but by deleting a property, you remove the reference to the object
foo = {bar: {baz: 31}};
delete foo.bar;            // remove only reference to "bar" object
// JS GC will now take over

所以,你不能删除对象。但是你可以删除对对象的引用,这可能会触发GC的对象删除。当然,局部变量(和全局变量)无法被删除。它们只是超出范围,取消关联任何相关对象,并潜在地释放内存,但仅当对象未返回或仍在其他地方引用时。

重要的是要意识到,因为JS不支持指针,所以GC可以是完美的。在任何时候,GC可以简单地遍历从任何范围内函数“看到”的所有对象集,并保证不会错过像循环等的任何东西。

只要您在功能风格中使用JS,并且不尝试构建将所有内容绑定在一起的全局对象,内存管理就不应该成为问题。


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