JavaScript:通过类方法创建和销毁类实例

53

我正在尝试找出如何通过类方法删除对象。我想创建一个具有销毁方法的类,以释放内存中的对象。到目前为止,我的研究还没有得出结论。我知道垃圾回收最终会处理对象,但我想要一种更明确的方式来销毁它。这可能吗?

// class constructor
var class = function () {
     this.destroy = function () {};
};

// instance
var instance = new class();
instance.destroy();
console.log(instance); // should be null or undefined

这种问题似乎源于对对象实例的生命周期理解上的误解。这并不是JS的工作方式,但这并不意味着它有局限性。这意味着需要用不同的方式来完成任务。那么你真正的目标是什么? - Nicole
2
我的目标是快速删除对象,包括事件处理程序和由创建对象而创建的任何内容。@NickC - user699242
检查一下我的答案,我认为你会从中得到你想要的一切。 - Mehran Hatami
我不是SO的常客,所以来得比较晚。经常看到人们试图限定类的范围,但是使用document.head.appendChild()将其加载到文档中。这将注册,并且eval() - 即使eval.call({},js)将使用窗口范围来查找类。 相反,不要将其添加到文档中(如果您正在这样做),而是使用类似于eval.call({},'class myclass{} (function(){ return new MyClass()})()')的东西评估整个类。 这不应该使用窗口,您可以随时替换MyClass。 - JonathanC
3个回答

69

1- 在JavaScript中,实际上没有办法销毁一个对象,但是可以使用delete来从对象中移除一个引用:

var obj = {};
obj.mypointer = null;
delete obj.mypointer;

2- delete关键词的重点是它实际上并不销毁对象,但是只有在删除指向同一对象的所有引用后,内存中没有其他指向该对象的引用,该对象才会被标记为可回收。 delete 关键字删除引用但不会垃圾回收实际对象。这意味着如果您有同一对象的多个引用,则仅在删除所有指向的引用后,该对象将立即被回收。

3- 当我们想确保我们不留下任何内存泄漏时,还有一些技巧和解决方法可以帮助我们。例如,如果您有一个由几个对象组成的数组,并且没有其他指向这些对象的引用,如果重新创建数组,则所有这些对象都将被删除。例如,如果您有var array = [{}, {}],那么像array = []这样覆盖数组的值将删除对数组内两个对象的引用,并且这两个对象将被标记为可回收。

4- 对于您的解决方案,最简单的方法就是这样:

var storage = {};
storage.instance = new Class();
//since 'storage.instance' is your only reference to the object, whenever you wanted to destroy do this:
storage.instance = null;
// OR
delete storage.instance;
如上所述,无论是将storage.instance = null还是delete storage.instance设置为,都足以删除对该对象的引用并允许GC清理它。不同之处在于,如果将其设置为null,那么存储对象仍然具有名为instance的属性(值为null)。如果您delete storage.instance,则存储对象不再具有名为instance的属性。
销毁方法呢? 这里的悖论点是,如果您在destroy函数中使用instance.destroy,则无法访问实际的instance指针,并且它不会让您删除它。
唯一的方法是将引用传递给destroy函数,然后将其删除:
// Class constructor
var Class = function () {
     this.destroy = function (baseObject, refName) {
         delete baseObject[refName];
     };
};

// instanciate
var storage = {};
storage.instance = new Class();
storage.instance.destroy(object, "instance");
console.log(storage.instance); // now it is undefined

但是如果我是你,我会简单地坚持第一个解决方案并像这样删除对象:

storage.instance = null;
// OR
delete storage.instance;

哇,太棒了 :)


1
@vsync 如果它没有附加到任何对象或变量,那么它将在完成运行任何代码后被收集。例如,如果您执行 (new SomeObject).doSomething),那么它将在执行doSomething后被收集。如果对象有 setTimeout 调用或任何其他触发异步逻辑的东西,则 SomeObject 将不会被收集,直到这些其他任务完成(除非这些任务不包含对 SomeObject 实例或 SomeObject 实例所属的任何其他内容的引用)。 - trusktr
1
假设 doSomething 方法设置了一个 setInterval,并且该方法执行 console.log(this),那么 setInterval 将一直运行,而 SomeObject 将永远不会被回收,除非 interval 有代码在某个时刻调用 clearInterval。基本上,如果没有任何代码(或正在运行的代码)与对象相关联,并且没有对它的引用,它将被回收。 - trusktr
这个答案是错误的。在JavaScript中,删除关键字会从一个对象中移除一个属性,但它并不直接释放内存或销毁该对象。如果真的这样做了,那么在调用delete之前将引用设置为null也就没有意义了。 - David Mills
@DavidMills 謝謝您讓我知道我從幾年前就有這個錯誤的答案,我做了一些更改,請問您能否再次檢查以確保現在是否合理? - Mehran Hatami
storage.instance的例子中,如果你想让GC清理instance对象,那么要么将storage.instance设置为null,要么使用delete storage.instance来删除对该对象的引用都可以。不同之处在于,如果你将其设置为null,则storage对象仍然具有名为instance的属性(值为null)。如果你使用delete storage.instance,则storage对象不再具有名为instance的属性。 - David Mills
@DavidMills 谢谢您的回复,我已将您的评论添加到答案中。 - Mehran Hatami

30

不需要。JavaScript会自动进行垃圾回收;只有在GC决定运行并且对象符合回收条件时,对象的内存才会被回收。

由于这将根据需要自动发生,那么显式回收内存的目的是什么呢?


13
我希望能够快速地删除对象创建时生成的事件处理程序、引用等内容。 - user699242
@user699242:为什么要这样做呢?如果除了对象本身没有其他引用指向这些东西,那么回收对象也将允许回收这些东西。您只需要“失去”所有对对象的引用即可,不需要手动处理。为什么您想要手动处理呢? - Jon
我猜“失去所有关于该对象的引用”对我来说不太清晰。那什么情况下会发生这种情况?抱歉,这一直让我感到困惑。 - user699242
换句话说,我需要做任何事情来“丢失引用”吗?例如将指针设置为null或... - user699242
@Jon 你知道我是否可以在自动收集时绑定一个回调函数吗?(当实例被销毁时运行) - philx_x
显示剩余3条评论

8
您只能手动删除对象的属性。因此:
var container = {};

container.instance = new class();

delete container.instance;

然而,这并不适用于其他指针。因此:
var container = {};

container.instance = new class();

var pointer = container.instance;

delete pointer; // false ( ie attempt to delete failed )

此外:
delete container.instance; // true ( ie attempt to delete succeeded, but... )

pointer; // class { destroy: function(){} }

实际上,删除仅用于删除对象属性本身,并不可靠地从内存中删除它们所指向的代码。

手动指定destroy方法可以解除任何事件监听器。类似这样:

function class(){
  this.properties = { /**/ }

  function handler(){ /**/ }

  something.addEventListener( 'event', handler, false );

  this.destroy = function(){
    something.removeEventListener( 'event', handler );
  }
}

1
这个吗 = void 0; 不是左手无效的赋值吗? - user699242
你说得完全正确。这也是一个非常愚蠢的想法,即使它能够工作,它也只会因为缺少指针而导致错误。 - Barney

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