Javascript对象销毁事件处理程序

3
MyMapStaticObject

var PlaceViewModel = function(){
    MyMapStaticObject.addLayer(someLayer);
}

PlaceViewModel.prototype.addMarker = function(item){

}

我有一个PlaceViewModel,它有一个名为addMarker的函数,用于向地图添加标记。我会在不同的类中使用PlaceViewModel的新实例。

var inst = new PlaceViewModel();

当我初始化PlaceViewModel时,通过MyMapStaticObject在地图上添加了一个新的图层。在实例销毁时,我应该移除这个图层。

我能否处理javascript的销毁事件?


请纠正我,只要有对它的引用,实例就不会被销毁。所以如果这是真的,那么你所要求的做法就没有意义(据我所知)。 - JLRishe
所有新实例都会向地图添加图层。因此,当对象被销毁时,我将从地图中删除已添加的图层。如果不删除,地图图层集合将充斥着图层。 - boliwe
我的意思是只要你有一个对象的引用,它就不会被销毁。因此,在对象被销毁后删除它是不可能的。或者我漏掉了什么? - JLRishe
2个回答

4

Javascript没有destroy事件。它是一种垃圾回收的语言,当不再有任何能够访问该对象引用的代码时,它将释放对象。当它释放对象时,它不会发出任何通知。

如果你想要实现一些清理代码以删除该层,则需要添加一个方法,当你完成这个对象时可以调用这个方法,以便在该方法中删除该层。调用这个方法将是你手动进行的操作(最可能是将其挂钩到其他正在进行的代码管理中,并在适当的时候通过该代码调用它)。


2
有点晚了,但我想知道一个对象何时被销毁。问题是JS没有内置的方法来做这件事。我想要这个功能是为了在页面上添加websocket事件,然后在进入另一个页面时删除它们。当然,我可以在页面加载部分实现这一点,但我有不同的框架,并想要一个更通用的解决方案来解决对象销毁的问题。Javascript是一种垃圾回收语言,但仍然很好地拥有一些我们可以附加到的生命周期事件。当然有代理,但这里也没有帮助,因为我需要知道已经被删除的代理本身。
好吧,有一个地方你会得到一种销毁事件,那就是MutationObserver,大多数现代浏览器现在都支持它。它不是严格意义上的销毁,而是从DOM中添加和删除节点。但一般来说,如果你有事件,你可能会有一些DOM节点可以附加,即使它是无视觉的,你也可以添加一个不可见的DOM元素。
所以我有一个叫做domDestroy(element,obj)的小函数,当它检测到element被删除时,它会检查obj是否有一个destroy方法,如果存在,它将调用它。
现在我遇到的一个问题是,我在一个隐藏的DOM节点中创建我的页面,当我将其放入可见的DOM节点中时,因为我从不可见的DOM节点中分离出来,然后附加到可见的DOM中,所以我得到了一个删除。这完全不是我们想要的。
解决方案非常简单,当进行这种双缓冲时,通常是在1步中完成的,例如隐藏当前页面,显示新页面。所以我会跟踪它何时被删除,并保留在一个简单的Set中,然后还会跟踪添加的元素,如果该元素是Set的一部分,我将删除它。然后我只需要在下一个tick上再次检查这个Set,如果它仍然存在,它就被真正删除了,我们调用对象的destroy方法。
下面是一个简单的示例,基本上如果你右键单击并检查页面,你可以通过拖放将LI上下移动,这将导致DOM分离和重新连接。但如果你删除其中一个LI,你会发现它说删除,因为它现在知道它没有重新连接到另一个DOM。
当然,要注意的一件事是,如果你做任何DOM元素的附加/分离,请尽量在同一个tick内完成,也就是说:要注意异步操作之间的情况。此外,你可能会使用分离的DOM来构建你的页面,在这里,你可以轻松地修改函数来处理这个问题,基本上使用destroyObserver.observe()添加这些。

const dsy = "__dom-destroy-obj";
const destroyList = new Set();
let tm;

function cleanUp() {
  tm = null;
  for (const el of destroyList) {
    for (const d of el[dsy]) {
      d.destroy();
    }
  }
  destroyList.clear();
}

function checkDestroy(el) {
  if (el[dsy]) {
    for (const d of el[dsy]) {
      if (!d.destroy) {
        console.warn("No destroy, on dom-destroy-obj target");
      } else {
        destroyList.add(el);
        if (tm) return; //already a timer running
        tm = setTimeout(cleanUp, 1);
      }
    }
  }
  if (el.childNodes) for (const n of el.childNodes) checkDestroy(n);
}

function checkAdded(el) {
  if (el[dsy]) {
    destroyList.delete(el);
  }
  if (el.childNodes) for (const n of el.childNodes) checkAdded(n);
}

const destroyObserver = new MutationObserver(
  function (mutations) {
    for (const m of mutations) {
      if (m.removedNodes.length) {
        for (const i of m.removedNodes) {
          checkDestroy(i);
        }
      }
      if (m.addedNodes.length) {
        for (const i of m.addedNodes) {
          checkAdded(i);
        }
      }
    }
  }
);

destroyObserver.observe(document.body, {
  childList: true,
  subtree: true
});

function domDestroy(element, obj) {
  if (!element[dsy]) element[dsy] = new Set();
  element[dsy].add(obj);
}


//simple test.

for (const i of document.querySelectorAll("li")) {
  domDestroy(i, {
    destroy: () => console.log("destroy")
  });
}
<span>
From your browsers inspector, try moving the LI's, and deleting them.  Only when you delete the DOM node, should the destroy method get called.
</span>


<ul>
  <li>Re order</li>
  <li>Or delete</li>
  <li>Some of these</li>
</ul>


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