如何创建一个检测布尔变量是否为真的事件监听器?

tru*_*ktr 7 javascript jquery events event-handling

例如,我有var menu_ready = false;.我有一个ajax函数,menu_ready当ajax完成时设置为true:

//set up event listener here

$(...).load(..., function() {
    ...
    menu_ready = true;
}
Run Code Online (Sandbox Code Playgroud)

如何设置等待为menu_ready真的事件监听器?

gil*_*ly3 8

您无法将事件侦听器附加到JavaScript变量本身,但您可以伪造它.取而代之的是布尔变种,使用对象有get,setlisten方法:

function Bool(initialValue) {
    var bool = !!initialValue;
    var listeners = [];
    var returnVal = function(value) {
        if (arguments.length) {
            var oldValue = bool;
            bool = !!value;
            listeners.forEach(function (listener, i, list) {
                listener.call(returnVal, { oldValue: oldValue, newValue: bool });
            });
        }
        return bool
    };
    returnVal.addListener = function(fn) {
        if (typeof fn == "function") {
            listeners.push(fn);
        }
        else {
            throw "Not a function!";
        }
    };
    return returnVal;
}
Run Code Online (Sandbox Code Playgroud)

你会这样使用它:

var menu_ready = Bool(false);
if (menu_ready()) {
    // this code won't get executed, because menu_ready() will currently return false;
}
menu_ready.addListener(function (e) {
    if (e.oldValue != e.newValue) {
        // value changed!
    }
});
menu_ready(true);  // listeners are called.
Run Code Online (Sandbox Code Playgroud)


jfr*_*d00 5

由于这是一个相当古老的问题,而且其中许多答案已经过时,我想我会添加一个更新的答案。

背景

Javascript 是一种事件驱动语言。因此,除了首次加载页面之外的任何时间,环境中发生的任何更改都是由某种事件引起的。因此,传达更改的最佳方式是使用事件。您要么侦听导致您想要观看的更改的同一事件,要么创建一个新事件以让人们知道何时发生了具体的变化。

Node.js 有一个非常有用的 eventEmitter 对象,它是纯 Javascript(不涉及 DOM)。如果想要一种独立于 DOM 的通用事件机制,也可以在浏览器中使用相同的代码。

浏览器 DOM 有自己的事件通知系统,您可以在其中创建事件、触发事件和侦听事件。

轮询更改(如此处接受的答案所示)通常是一种效率低下的“黑客”,可能会错过更改,不一定及时,浪费电池和 CPU 等......如果您控制代码,总是有更好的设计它的方式比轮询。

OP的实际问题

仍然没有有效的机制来监控变量变化,但 OP 的问题实际上是有人想知道这段代码何时被触发:

$(...).load(..., function() {
    ...
    menu_ready = true;
}
Run Code Online (Sandbox Code Playgroud)

当然,一个简单的答案是,任何想知道何时准备就绪的人也可以只监视与 相同的事情$(...).load(),但这会在某种程度上将他们的通知与具体实施方式联系起来。

创建我们自己的任何人都可以收听的事件通知

相反,更好的是创建我们自己的事件,当我们知道菜单准备好时,我们触发该事件。然后代码中任何地方的任何人都可以侦听同一事件。

浏览器已经建成描述一个可扩展的事件系统在这里。所以,我们可以在这里使用它。您需要选择一些众所周知的对象来触发任何侦听器想要注册以了解此情况时存在的事件。由于 OP 没有显示任何特定的上下文,我将只选择window对象。

$(...).load(..., function() {
    ...
    menu_ready = true;

    // create new event
    var event = new Event('menu_ready');

    // Dispatch the event.
    window.dispatchEvent(event);        
 }
Run Code Online (Sandbox Code Playgroud)

请注意,您可以使用任何 DOM 对象作为事件源(任何对您的应用程序最有意义的对象),只要它在有人想要注册事件的时间和事件发生的时间都存在。

然后,代码中任何位置的侦听器都可以侦听此事件:

 window.addEventListener("menu_ready", function() {
     console.log("menu is now ready");
 });
Run Code Online (Sandbox Code Playgroud)

工作演示

这是一个实际的工作实现,它在按下按钮时触发事件。

$(...).load(..., function() {
    ...
    menu_ready = true;
}
Run Code Online (Sandbox Code Playgroud)
$(...).load(..., function() {
    ...
    menu_ready = true;

    // create new event
    var event = new Event('menu_ready');

    // Dispatch the event.
    window.dispatchEvent(event);        
 }
Run Code Online (Sandbox Code Playgroud)
 window.addEventListener("menu_ready", function() {
     console.log("menu is now ready");
 });
Run Code Online (Sandbox Code Playgroud)


我精心制作了这个答案以匹配这里问题中说明的原始问题。作为参考,还有其他几种有用的查看变化的机制,但它们通常依赖于对象的属性,而不仅仅是一个简单的变量。

查看有关此问题的所有答案:Listing for variable changes。但特别要确保你阅读了这两个:

使用代理对象来跟踪更改

使用 getter 和 setter 来跟踪更改


Jon*_*n M -2

警告我下面的回答来自五月花号启航前不久的一段时间。这是一个糟糕的答案。我无法删除它,因为它已被接受,并且系统不允许我删除已接受的答案。我可以编辑它以使其更好,但是当其他人在下面给出了可靠的答案时为什么还要这样做呢?底线:跳过这一切,走向更充实的生活。:)

<BAD-ANSWER>
Run Code Online (Sandbox Code Playgroud)

一种方法是持续轮询:

function checkMenu() {
    if (!menu_ready) {
        setTimeout("checkMenu();", 1000);
        return;
    } else {
        // menu_ready is true, so do what you need to do here.
    }
}
Run Code Online (Sandbox Code Playgroud)

和...

<body onload="checkMenu();">
Run Code Online (Sandbox Code Playgroud)
</BAD-ANSWER>
Run Code Online (Sandbox Code Playgroud)

  • 不不不...不是一个好主意...如果您无法控制布尔值,只需附加一个 DOM 操作事件侦听器或其他侦听器。 (4认同)
  • @Jonathan,实际上我认为你的例子中的 `setTimeout("checkMenu();", 1000);` 应该是 `setTimeout(checkMenu, 1000);` 才能工作,但我明白你的意思。 (2认同)