在javascript中可以阻止已失效的侦听器吗? [英] Are lapsed listeners preventable in javascript?

查看:99
本文介绍了在javascript中可以阻止已失效的侦听器吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的问题是javascript中是否可以阻止失效的侦听器问题?但显然问题这个词会导致问题。

My question is really "Is the lapsed listener problem preventable in javascript?" but apparently the word "problem" causes a problem.

维基百科页面说明失效的听众问题可以通过持有弱参考给观察者。我之前在Java中已经实现了它并且它运行良好,我认为我将在Javascript中实现它,但现在我不知道如何。 javascript甚至有弱引用吗?我看到 WeakSet WeakMap 在名称中有弱,但它们似乎不是对此有帮助,据我所见。

The wikipedia page says the lapsed listener problem can be solved by the subject holding weak references to the observers. I've implemented that before in Java and it works nicely, and I thought I'd implement it in Javascript, but now I don't see how. Does javascript even have weak references? I see there are WeakSet and WeakMap which have "Weak" in their names, but they don't seem to be helpful for this, as far as I can see.

这是一个 jsfiddle 显示问题的典型案例。

Here's a jsfiddle showing a typical case of the problem.

html:

<div id="theCurrentValueDiv">current value: false</div>
<button id="thePlusButton">+</button>

javascript:

The javascript:

'use strict';
console.log("starting");
let createListenableValue = function(initialValue) {
  let value = initialValue;
  let listeners = [];
  return {
    // Get the current value.
    get: function() {
      return value;
    },
    // Set the value to newValue, and call listener()
    // for each listener that has been added using addListener().
    set: function(newValue) {
      value = newValue;
      for (let listener of listeners) {
        listener();
      }
    },
    // Add a listener that set(newValue) will call with no args
    // after setting value to newValue.
    addListener: function(listener) {
      listeners.push(listener);
      console.log("and now there "+(listeners.length==1?"is":"are")+" "+listeners.length+" listener"+(listeners.length===1?"":"s"));
    },
  };
};  // createListenable

let theListenableValue = createListenableValue(false);

theListenableValue.addListener(function() {
  console.log("    label got value change to "+theListenableValue.get());
  document.getElementById("theCurrentValueDiv").innerHTML = "current value: "+theListenableValue.get();
});

let nextControllerId = 0;

let thePlusButton = document.getElementById("thePlusButton");
thePlusButton.addEventListener('click', function() {
  let thisControllerId = nextControllerId++;
  let anotherDiv = document.createElement('div');
  anotherDiv.innerHTML = '<button>x</button><input type="checkbox"> controller '+thisControllerId;
  let [xButton, valueCheckbox] = anotherDiv.children;
  valueCheckbox.checked = theListenableValue.get();
  valueCheckbox.addEventListener('change', function() {
    theListenableValue.set(valueCheckbox.checked);
  });

  theListenableValue.addListener(function() {
    console.log("    controller "+thisControllerId+" got value change to "+theListenableValue.get());
    valueCheckbox.checked = theListenableValue.get();
  });

  xButton.addEventListener('click', function() {
    anotherDiv.parentNode.removeChild(anotherDiv);
    // Oh no! Our listener on theListenableValue has now lapsed;
    // it will keep getting called and updating the checkbox that is no longer
    // in the DOM, and it will keep the checkbox object from ever being GCed.
  });

  document.body.insertBefore(anotherDiv, thePlusButton);
});

在这个小提琴中,可观察状态是一个布尔值,你可以添加和删除查看的复选框并控制它,所有听众都保持同步。
问题是当你删除其中一个控制器时,它的监听器不会消失:监听器不断被调用并更新控制器复选框并阻止复选框被GCed,即使复选框不再存在DOM,否则是GCable。您可以在javascript控制台中看到这种情况,因为侦听器回调会将消息输出到控制台。

In this fiddle, the observable state is a boolean value, and you can add and remove checkboxes that view and control it, all kept in sync by listeners on it. The problem is that when you remove one of the controllers, its listener doesn't go away: the listener keeps getting called and updating the controller checkbox and prevents the checkbox from being GCed, even though the checkbox is no longer in the DOM and is otherwise GCable. You can see this happening in the javascript console since the listener callback prints a message to the console.

我想要的是控制器DOM节点及其相关联当我从DOM中删除节点时,值侦听器变为GCable。从概念上讲,DOM节点应该拥有侦听器,而observable应该拥有对侦听器的弱引用。有没有一种干净的方法来实现这一目标?

What I'd like instead is for the controller DOM node and its associated value listener to become GCable when I remove the node from the DOM. Conceptually, the DOM node should own the listener, and the observable should hold a weak reference to the listener. Is there a clean way to accomplish that?

我知道我可以通过使 x 按钮显式删除侦听器以及DOM子树,但是如果应用程序中的某些其他代码稍后删除包含我的控制器节点的DOM的部分,则无效,例如执行 document.body.innerHTML =''。我想要进行设置,以便在发生这种情况时,我创建的所有DOM节点和侦听器都会被释放并变为GCable。有办法吗?

I know I can fix the problem in my fiddle by making the x button explicitly remove the listener along with the DOM subtree, but that doesn't help in the case that some other code in the app later removes part of the DOM containing my controller node, e.g. by executing document.body.innerHTML = ''. I'd like set things up so that, when that happens, all the DOM nodes and listeners I created get released and become GCable. Is there a way?

推荐答案

你可以使用变异观察者


提供了注意对DOM树所做的更改。它被设计为旧的Mutation Events功能的替代品,它是DOM3 Events规范的一部分。

provides the ability to watch for changes being made to the DOM tree. It is designed as a replacement for the older Mutation Events feature which was part of the DOM3 Events specification.

这是一个如何做的例子使用可以在有载的代码中找到

An example of how this can be used can be found in the code for on-load

if (window && window.MutationObserver) {
  var observer = new MutationObserver(function (mutations) {
    if (Object.keys(watch).length < 1) return
    for (var i = 0; i < mutations.length; i++) {
      if (mutations[i].attributeName === KEY_ATTR) {
        eachAttr(mutations[i], turnon, turnoff)
        continue
      }
      eachMutation(mutations[i].removedNodes, function (index, el) {
        if (!document.documentElement.contains(el)) turnoff(index, el)
      })
      eachMutation(mutations[i].addedNodes, function (index, el) {
        if (document.documentElement.contains(el)) turnon(index, el)
      })
    }
  })

  observer.observe(document.documentElement, {
    childList: true,
    subtree: true,
    attributes: true,
    attributeOldValue: true,
    attributeFilter: [KEY_ATTR]
  })
}

这篇关于在javascript中可以阻止已失效的侦听器吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆