是否有可能在JS中有一个事件,当某个变量的值发生变化时触发?JQuery被接受。


不是直接的:你需要一个“addListener/removeListener”接口的一对getter/setter…或者一个NPAPI插件(但这完全是另一个故事)。


No.

但是,如果这真的很重要,你有两个选择(第一个是测试,第二个不是):

首先,使用setter和getter,如下所示:

var myobj = {a : 1};

function create_gets_sets(obj) { // make this a framework/global function
    var proxy = {}
    for ( var i in obj ) {
        if (obj.hasOwnProperty(i)) {
            var k = i;
            proxy["set_"+i] = function (val) { this[k] = val; };
            proxy["get_"+i] = function ()    { return this[k]; };
        }
    }
    for (var i in proxy) {
        if (proxy.hasOwnProperty(i)) {
            obj[i] = proxy[i];
        }
    }
}

create_gets_sets(myobj);

然后你可以这样做:

function listen_to(obj, prop, handler) {
    var current_setter = obj["set_" + prop];
    var old_val = obj["get_" + prop]();
    obj["set_" + prop] = function(val) { current_setter.apply(obj, [old_val, val]); handler(val));
}

然后像这样设置监听器:

listen_to(myobj, "a", function(oldval, newval) {
    alert("old : " + oldval + " new : " + newval);
}

其次,你可以把手表的价值:

给定上面的myobj,上面有'a':

function watch(obj, prop, handler) { // make this a framework/global function
    var currval = obj[prop];
    function callback() {
        if (obj[prop] != currval) {
            var temp = currval;
            currval = obj[prop];
            handler(temp, currval);
        }
    }
    return callback;
}

var myhandler = function (oldval, newval) {
    //do something
};

var intervalH = setInterval(watch(myobj, "a", myhandler), 100);

myobj.set_a(2);

正如Luke Schafer的回答(注意:这是指他的原始帖子;但是这里的整个观点在编辑之后仍然有效),我还建议使用一对Get/Set方法来访问你的值。

然而,我想建议一些修改(这就是为什么我在这里发帖……)

这段代码的一个问题是对象myobj的字段A是可以直接访问的,所以可以在不触发侦听器的情况下访问/更改它的值:

var myobj = { a : 5, get_a : function() { return this.a;}, set_a : function(val) { this.a = val; }}
/* add listeners ... */
myobj.a = 10; // no listeners called!

封装

因此,为了保证监听器被实际调用,我们必须禁止直接访问字段a。如何做到这一点?使用闭包!

var myobj = (function() { // Anonymous function to create scope.

    var a = 5;            // 'a' is local to this function
                          // and cannot be directly accessed from outside
                          // this anonymous function's scope

    return {
        get_a : function() { return a; },   // These functions are closures:
        set_a : function(val) { a = val; }  // they keep reference to
                                            // something ('a') that was on scope
                                            // where they were defined
    };
})();

现在您可以使用Luke建议的相同方法来创建和添加侦听器,但是您可以放心,没有可能的方法来读取或写入一个未被注意到的对象!

以编程方式添加封装的字段

继续Luke的思路,我现在提出一种简单的方法,通过简单的函数调用将封装的字段和各自的getter /setter添加到对象中。

注意,这只适用于值类型。为了与引用类型一起工作,必须实现某种深度复制(例如,请参阅这个)。

function addProperty(obj, name, initial) {
    var field = initial;
    obj["get_" + name] = function() { return field; }
    obj["set_" + name] = function(val) { field = val; }
}

这与之前的工作相同:我们在函数上创建一个局部变量,然后创建一个闭包。

如何使用它?简单:

var myobj = {};
addProperty(myobj, "total", 0);
window.alert(myobj.get_total() == 0);
myobj.set_total(10);
window.alert(myobj.get_total() == 10);

如果你正在使用jQuery {UI}(每个人都应该使用:-)),你可以使用.change()和一个隐藏的<input/>元素。


对于那些几年后收听的人来说:

大多数浏览器(和IE6+)都有一个解决方案,它使用onpropertychange事件和更新的规范defineProperty。有一点需要注意的是,您需要将变量设置为dom对象。

详情:

http://johndyer.name/native-browser-get-set-properties-in-javascript/


很抱歉提出了一个旧的话题,但对于那些(像我一样!)不明白Eli Grey的例子是如何工作的人来说,这里有一个小手册:

var test = new Object();
test.watch("elem", function(prop,oldval,newval){
    //Your code
    return newval;
});

希望这能帮助到一些人


AngularJS(我知道这不是JQuery,但这可能有帮助。[纯JS只在理论上是好的]):

$scope.$watch('data', function(newValue) { ..

其中“data”是作用域内变量的名称。

这里有一个doc的链接。


你正在寻找的功能可以通过使用“defineProperty()”方法来实现——这只适用于现代浏览器:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

我写了一个jQuery扩展,有一些类似的功能,如果你需要更多的跨浏览器支持:

https://github.com/jarederaj/jQueue

对象的队列回调的jQuery小扩展 变量、对象或键的存在。你可以分配任意数量的 对可能受影响的任意个数的数据点的回调 进程在后台运行。jQueue监听并等待 您指定的这些数据开始存在,然后发射 纠正回调函数的参数。


//ex:
/*
var x1 = {currentStatus:undefined};
your need is x1.currentStatus value is change trigger event ?
below the code is use try it.
*/
function statusChange(){
    console.log("x1.currentStatus_value_is_changed"+x1.eventCurrentStatus);
};

var x1 = {
    eventCurrentStatus:undefined,
    get currentStatus(){
        return this.eventCurrentStatus;
    },
    set currentStatus(val){
        this.eventCurrentStatus=val;
      //your function();
    }
};

or

/*  var x1 = {
eventCurrentStatus:undefined,
currentStatus : {
    get : function(){
        return Events.eventCurrentStatus
        },
    set : function(status){
        Events.eventCurrentStatus=status;

    },
}*/
console.log("eventCurrentStatus = "+ x1.eventCurrentStatus);
x1.currentStatus="create"
console.log("eventCurrentStatus = "+ x1.eventCurrentStatus);
x1.currentStatus="edit"
console.log("eventCurrentStatus = "+ x1.eventCurrentStatus);
console.log("currentStatus = "+ x1.currentStatus);

or

/* global variable ku*/
    var jsVarEvents={};
    Object.defineProperty(window, "globalvar1", {//no i18n
        get: function() { return window.jsVarEvents.globalvarTemp},
        set: function(value) { window.window.jsVarEvents.globalvarTemp = value; }
    });
    console.log(globalvar1);
    globalvar1=1;
    console.log(globalvar1);

Utils = {
    eventRegister_globalVariable : function(variableName,handlers){
        eventRegister_JsonVariable(this,variableName,handlers);
    },
    eventRegister_jsonVariable : function(jsonObj,variableName,handlers){
        if(jsonObj.eventRegisteredVariable === undefined) {
            jsonObj.eventRegisteredVariable={};//this Object is used for trigger event in javascript variable value changes ku
        }
        Object.defineProperty(jsonObj, variableName , {
                    get: function() { 
                        return jsonObj.eventRegisteredVariable[variableName] },
                    set: function(value) {
                        jsonObj.eventRegisteredVariable[variableName] = value; handlers(jsonObj.eventRegisteredVariable[variableName]);}
                    });
            }

使用Prototype: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

/ /控制台 函数print(t) { var c = document.getElementById('console'); c.innerHTML = c.innerHTML + '<br />' + t; } / /演示 var myVar = 123; Object.defineProperty(this, 'varWatch', { get: function(){返回myVar;}, 集合:函数(v) { myVar = v; 打印(“价值变化!新值:' + v); } }); 打印(varWatch); varWatch = 456; 打印(varWatch); < pre id =“控制台”> < / >之前

其他的例子

// Console function print(t) { var c = document.getElementById('console'); c.innerHTML = c.innerHTML + '<br />' + t; } // Demo var varw = (function (context) { /** * Declare a new variable. * * @param {string} Variable name. * @param {any | undefined} varValue Default/Initial value. * You can use an object reference for example. */ return function (varName, varValue) { var value = varValue; Object.defineProperty(context, varName, { get: function () { return value; }, set: function (v) { value = v; print('Value changed! New value: ' + value); } }); }; })(window); varw('varWatch'); // Declare without initial value print(varWatch); varWatch = 456; print(varWatch); print('---'); varw('otherVarWatch', 123); // Declare with initial value print(otherVarWatch); otherVarWatch = 789; print(otherVarWatch); <pre id="console"> </pre>


一个相当简单和简单的解决方案是只使用函数调用来设置全局变量的值,而不要直接设置它的值。这样你就可以完全控制:

var globalVar;

function setGlobalVar(value) {
    globalVar = value;
    console.log("Value of globalVar set to: " + globalVar);
    //Whatever else
}

没有办法强制执行,它只需要编程纪律……尽管你可以使用grep(或类似的东西)来检查你的代码没有直接设置globalVar的值。

或者你可以把它封装在一个对象和用户getter和setter方法中…只是一个想法。


是的,现在这是完全可能的!

我知道这是一个旧线程,但现在这种效果是可能使用访问器(getter和setter): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#Defining_getters_and_setters

你可以像这样定义一个对象,其中inner表示字段a:

x = {
  aInternal: 10,
  aListener: function(val) {},
  set a(val) {
    this.aInternal = val;
    this.aListener(val);
  },
  get a() {
    return this.aInternal;
  },
  registerListener: function(listener) {
    this.aListener = listener;
  }
}

然后你可以使用下面的方法注册一个监听器:

x.registerListener(function(val) {
  alert("Someone changed the value of x.a to " + val);
});

因此,每当x.a的值发生变化时,监听器函数就会被触发。运行下面的代码行将弹出警告:

x.a = 42;

请看一个例子:https://jsfiddle.net/5o1wf1bn/1/

您还可以使用一个侦听器数组,而不是单个侦听器插槽,但是我想给您一个最简单的示例。


这个问题最初发布于2009年,现有的大多数答案要么过时,无效,要么需要包含庞大的库:

对象。观看和反对。Observe都已弃用,不应该使用。 onPropertyChange是一个DOM元素事件处理程序,只在某些版本的IE中工作。 object . defineproperty允许您将对象属性设置为不可变的,这将允许您检测尝试的更改,但它也将阻止任何更改。 定义setter和getter是可行的,但是它需要大量的设置代码,并且当您需要删除或创建新属性时,它不能很好地工作。

截至2018年,您现在可以使用代理对象来监视(和拦截)对对象的更改。它是专门为OP试图做的事情而建造的。这里有一个基本的例子:

var targetObj = {};
var targetProxy = new Proxy(targetObj, {
  set: function (target, key, value) {
      console.log(`${key} set to ${value}`);
      target[key] = value;
      return true;
  }
});

targetProxy.hello_world = "test"; // console: 'hello_world set to test'

Proxy对象唯一的缺点是:

Proxy对象在旧的浏览器(如IE11)中不可用,而且polyfill不能完全复制Proxy功能。 代理对象对于特殊对象(例如Date)并不总是表现得像预期的那样——代理对象最好与普通对象或数组配对。

如果你需要观察对嵌套对象所做的更改,那么你需要使用一个专门的库,如Observable Slim(我已经发布了)。它是这样工作的:

var test = {testing:{}};
var p = ObservableSlim.create(test, true, function(changes) {
    console.log(JSON.stringify(changes));
});

p.testing.blah = 42; // console:  [{"type":"add","target":{"blah":42},"property":"blah","newValue":42,"currentPath":"testing.blah",jsonPointer:"/testing/blah","proxy":{"blah":42}}]

 

这是不可能的。

但是,这可以使用CustomEvent: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent来完成

下面的方法接受一个变量名数组作为输入,并为每个变量添加事件侦听器,并在变量值发生任何更改时触发事件。

该方法使用轮询来检测值的变化。您可以以毫秒为单位增加timeout的值。

function watchVariable(varsToWatch) {
    let timeout = 1000;
    let localCopyForVars = {};
    let pollForChange = function () {
        for (let varToWatch of varsToWatch) {
            if (localCopyForVars[varToWatch] !== window[varToWatch]) {
                let event = new CustomEvent('onVar_' + varToWatch + 'Change', {
                    detail: {
                        name: varToWatch,
                        oldValue: localCopyForVars[varToWatch],
                        newValue: window[varToWatch]
                    }
                });
                document.dispatchEvent(event);
                localCopyForVars[varToWatch] = window[varToWatch];
            }
        }
        setTimeout(pollForChange, timeout);
    };
    let respondToNewValue = function (varData) {
        console.log("The value of the variable " + varData.name + " has been Changed from " + varData.oldValue + " to " + varData.newValue + "!!!"); 
    }
    for (let varToWatch of varsToWatch) {
        localCopyForVars[varToWatch] = window[varToWatch];
        document.addEventListener('onVar_' + varToWatch + 'Change', function (e) {
            respondToNewValue(e.detail);
        });
    }
    setTimeout(pollForChange, timeout);
}

通过调用方法:

watchVariables(['username', 'userid']);

它将检测变量username和userid的更改。


这是一个老线程,但我在使用Angular寻找解决方案时偶然发现了第二高的答案(自定义侦听器)。虽然这个解决方案是可行的,但angular有一个更好的内置方法来解决这个问题,使用@Output和事件发射器。在自定义监听器回答中的例子:

ChildComponent.html

<button (click)="increment(1)">Increment</button>

ChildComponent.ts

import {EventEmitter, Output } from '@angular/core';

@Output() myEmitter: EventEmitter<number> = new EventEmitter<number>();

private myValue: number = 0;

public increment(n: number){
  this.myValue += n;

  // Send a change event to the emitter
  this.myEmitter.emit(this.myValue);
}

ParentComponent.html

<child-component (myEmitter)="monitorChanges($event)"></child-component>
<br/>
<label>{{n}}</label>

ParentComponent.ts

public n: number = 0;

public monitorChanges(n: number){
  this.n = n;
  console.log(n);
}

现在每次单击子按钮时都会更新非父按钮。工作stackblitz


请记住,最初的问题是针对变量的,而不是针对对象的;)

除了上面所有的答案,我创建了一个名为thewatch .js的小库, 在javascript中使用相同的方法来捕捉和回调普通全局变量的变化。

与JQUERY变量兼容,不需要使用object,如果需要,可以直接传递几个变量的ARRAY。

如果有帮助的话……: https://bitbucket.org/esabora/forthewatch基本上你只需要调用这个函数: watchIt(“theVariableToWatch”、“varChangedFunctionCallback”);

如果不相关,先说句抱歉。


在getter和setter的帮助下,您可以定义一个JavaScript类来做这样的事情。

首先,我们定义一个名为MonitoredVariable的类:

class MonitoredVariable {
  constructor(initialValue) {
    this._innerValue = initialValue;
    this.beforeSet = (newValue, oldValue) => {};
    this.beforeChange = (newValue, oldValue) => {};
    this.afterChange = (newValue, oldValue) => {};
    this.afterSet = (newValue, oldValue) => {};
  }

  set val(newValue) {
    const oldValue = this._innerValue;
    // newValue, oldValue may be the same
    this.beforeSet(newValue, oldValue);
    if (oldValue !== newValue) {
      this.beforeChange(newValue, oldValue);
      this._innerValue = newValue;
      this.afterChange(newValue, oldValue);
    }
    // newValue, oldValue may be the same
    this.afterSet(newValue, oldValue);
  }

  get val() {
    return this._innerValue;
  }
}

假设我们想监听钱的变化,让我们创建一个初始值为0的MonitoredVariable实例:

const money = new MonitoredVariable(0);

然后我们可以使用money来获取或设置它的值。

console.log(money.val); // Get its value
money.val = 2; // Set its value

因为我们没有为它定义任何监听器,所以在钱之后没有什么特别的事情发生。Val变成了2。

现在让我们定义一些侦听器。我们有四个可用的侦听器:beforeSet、beforeChange、afterChange、afterSet。 当你使用金钱时,下面的事情会依次发生。val = newValue更改变量的值:

钱。beforeSet (newValue oldValue); 钱。beforeChange (newValue oldValue);(如果它的值没有改变,将被跳过) 钱。val = newValue; 钱。afterChange (newValue oldValue);(如果它的值没有改变,将被跳过) 钱。afterSet (newValue oldValue);

现在我们定义了afterChange监听器,它只会在金钱之后被触发。val已经改变(而afterSet将被触发,即使新值与旧值相同):

money.afterChange = (newValue, oldValue) => {
  console.log(`Money has been changed from ${oldValue} to ${newValue}`);
};

现在设置一个新的值3,看看会发生什么:

money.val = 3;

您将在控制台中看到以下内容:

Money has been changed from 2 to 3

完整代码请参见https://gist.github.com/yusanshi/65745acd23c8587236c50e54f25731ab。


我找到了最简单的方法,从这个答案开始:

// variable holding your data
const state = {
  count: null,
  update() {
    console.log(`this gets called and your value is ${this.pageNumber}`);
  },
  get pageNumber() {
    return this.count;
  },
  set pageNumber(pageNumber) {
    this.count = pageNumber;
    // here you call the code you need
    this.update(this.count);
  }
};

然后:

state.pageNumber = 0;
// watch the console

state.pageNumber = 15;
// watch the console

在我的情况下,我试图找出我的项目中包含的任何库是否重新定义了我的window.player。所以,在我的代码的开始,我只是做了:

Object.defineProperty(window, 'player', {
  get: () => this._player,
  set: v => {
    console.log('window.player has been redefined!');
    this._player = v;
  }
});

最近我发现自己也有同样的问题。想要监听变量的变化并在变量变化时做一些事情。

有人提出了一个使用setter设置值的简单解决方案。

声明一个简单的对象来保存变量的值:

var variableObject = {
    value: false,
    set: function (value) {
        this.value = value;
        this.getOnChange();
    }
}

对象包含一个set方法,通过该方法可以更改值。但是它也调用了一个getOnChange()方法。将定义它。

variableObject.getOnChange = function() {
    if(this.value) {
        // do some stuff
    }
}

现在,每当我做variableObject.set(true)时,getOnChange方法就会触发,如果值被设置为所需的值(在我的例子中:true), if块也会执行。

这是我发现的最简单的方法。


我来这里是为了寻找node js的相同答案。就在这里

const events = require('events');
const eventEmitter = new events.EventEmitter();

// Createing state to watch and trigger on change
let x = 10 // x is being watched for changes in do while loops below

do {
    eventEmitter.emit('back to normal');
}
while (x !== 10);

do {
    eventEmitter.emit('something changed');
}
while (x === 10);

我正在做的是设置一些事件发射器时,值被改变,并使用do while循环来检测它。


这就是我所做的:调用JSON。Stringify两次并比较两个字符串…

缺点:

你只能知道整个物体是否发生了变化 您必须手动检测更改 你最好在对象中只有基本字段(没有属性,没有函数……)


我搜索了JavaScript双向数据绑定库,发现了这个库。

我没有成功地使它在DOM到变量的方向上工作,但在变量到DOM的方向上它是工作的,这就是我们在这里需要的。

我稍微重写了一下,因为原始代码(对我来说)很难阅读。它使用 object。defineproperty,这是Eliot b给出的第二多赞的答案,至少有部分错误。

<!DOCTYPE html>
<html>
  <head>
    <title>TODO supply a title</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script>
        const dataBind = (function () {
            const getElementValue = function (selector) {
                let element = document.querySelectorAll(selector)[0];
                return 'value' in element ? element.value : element.innerHTML;
            };
            const setElementValue = function (selector, newValue) {
                let elementArray = document.querySelectorAll(selector);
                for (let i = 0; i < elementArray.length; i++) {
                    let element = elementArray[i];
                    if ('value' in element) {
                        element.value = newValue;
                        if (element.tagName.toLowerCase() === 'select'){
                            let options = element.querySelectorAll('option');
                            for (let option in options){
                                if (option.value === newValue){
                                    option.selected = true;
                                    break;
                                }
                            }
                        }
                    } else {
                        element.innerHTML = newValue;
                    }
                }
            };

            const bindModelToView = function (selector, object, property, enumerable) {
                Object.defineProperty(object, property, {
                    get: function () {
                        return getElementValue(selector);
                    },
                    set: function (newValue) {
                        setElementValue(selector, newValue);
                    },
                    configurable: true,
                    enumerable: (enumerable)
                });
            };
            return {
                bindModelToView
            };
        })();
    </script>
</head>
<body>
    <div style="padding: 20%;">
        <input  type="text" id="text" style="width: 40px;"/>
    </div>
    <script>
        let x = {a: 1, b: 2};
        dataBind.bindModelToView('#text', x, 'a'); //data to dom

        setInterval(function () {
             x.a++;
        }, 1000);
   </script> 
</body>

</html>

JSFiddle.

JSFiddle原始代码。

在提供的示例中,对象x的属性由setInterval更新,文本输入的值也会自动更新。如果这还不够,并且事件是你想要的,你可以在上面的输入中添加onchange监听器。如果需要,输入也可以被隐藏。


这个问题是关于变量的,而不是对象属性!因此,我的方法是利用窗口对象及其自定义getter /setter,然后像“正常”变量(不像对象属性)一样使用/更改变量。

最简单的方法是@José Antonio Postigo在他的回答中(我投票了那个答案)。我在这里想做的是将其简化为一个更简单的“creator”函数(这样即使不理解对象getter /setter的人也可以轻松使用它)。

一个活生生的例子在这里:https://codepen.io/dimvai/pen/LYzzbpz

这是你必须拥有的一般“creator”函数:

let createWatchedVariable = (variableName,initialValue,callbackFunction) => {
    // set default callback=console.log if missing
    callbackFunction ??= function(){console.log(variableName+" changed to " + window[variableName])};
    // the actual useful code:
    Object.defineProperty(window, variableName, {
      set: function(value) {window["_"+variableName] = value; callbackFunction()},
      get: function() {return window["_"+variableName]}
    });
    window[variableName]=initialValue??null;
};

然后,不要使用var或let来声明变量,而是使用下面的语句:

// 1st approach - default callback//    
createWatchedVariable ('myFirstVariable',12);  
// instead of: let myFirstVariable = 12;

或者,为了使用你的自定义回调(而不是默认的console.log)使用:

// 2nd approach - set a custom callback//
var myCallback = ()=>{/*your custom code...*/}
// now use callback function as the third optional argument
createWatchedVariable('mySecondVariable',0,myCallback);

就是这样!现在,你可以像改变一个“正常”变量一样改变它:

myFirstVariable = 15;      // logs to console
myFirstVariable++;         // logs to console
mySecondVariable = 1001;   // executes your custom code
mySecondVariable++;        // executes your custom code

基于akira的回答,我补充说,你可以通过listerner操纵dom。

https://jsfiddle.net/2zcr0Lnh/2/

javascript:

x = {
  aInternal: 10,
  aListener: function(val) {},
  set a(val) {
    this.aInternal = val;
    this.aListener(val);
  },
  get a() {
    return this.aInternal;
  },
  registerListener: function(listener) {
    this.aListener = listener;
  }
}

x.registerListener(function(val) {
document.getElementById('showNumber').innerHTML = val;
});


x.a = 50;

function onClick(){
x.a = x.a + 1;
}

html:

<div id="showNumber">
 
</div>


<button onclick="onClick()">
click me to rerender
</button>

当变量x.a发生变化时,registerListener方法将被触发。


@akira和@mo-d-genesis的解决方案可以进一步简化,因为在这个例子中DOM操作不依赖于状态:

CodePen

const render = (val) => {
  document.getElementById("numberDiv").innerHTML = val;
};

state = {
  _value_internal: undefined,
  set value(val) {
    // 1. set state value
    this._value_internal = val;
    // 2. render user interface
    render(val);
  },
  get value() {
    return this._value_internal;
  },
};

const onClick = () => {
  state.value = state.value + 1; // state change leads to re-render!
};

// set default value
state.value = 0;

对应的html:

<div id="numberDiv"></div>

<button onclick="onClick()">
  Click to rerender
</button>

备注:

我重命名了变量和函数,以更好地反映它们的语义。 仅供参考:通过改变变量,Svelte提供了非常类似的反应行为


这不是一个理想的答案,但它所做的是在JavaScript中每100毫秒设置一个间隔,并检查变量是否被改变,当它被改变时,它会做一些事情(OP想要的任何事情),然后清除间隔,所以它有点模拟OP的要求。

let myvar = "myvar";

const checkChange = setInterval(() => {
    if (myvar !== "myvar") {
        console.log("My precious var has been changed!");
        clearInterval(checkChange);
    }
}, 100);

现在,如果myvar被更改为其他东西,那么这个程序会说“我宝贵的var已被更改!”:)


这是一个古老而伟大的问题,已经超过12年了。此外,有很多方法来解决它。然而,大多数都是复杂的或使用旧的JS概念,我们在2022年,我们可以使用ES6来改进我们的代码。

我将实现我经常使用的两个主要解决方案。

简单的变量

如果我们有一个简单的变量,我们不关心重用,那么我们可以将变量声明为一个对象。我们定义了一个set和get方法以及一个listener属性来处理“change”事件。

const $countBtn = document.getElementById('counter') const $output = document.getElementById('output') const counter = { v: 0, listener: undefined, set value(v) { this.v = v if (this.listener) this.listener(v) }, get value() { return this.v }, count() { this.value++ }, registerListener(callback) { this.listener = callback }, } const countOnClick = () => { counter.count() } $countBtn.onclick = countOnClick counter.registerListener(v => { $output.textContent = v }) counter.value = 50 #output { display: block; font-size: 2em; margin-top: 0.67em; margin-bottom: 0.67em; margin-left: 0; margin-right: 0; font-weight: bold; } <button id="counter">Count</button> <div id="output"></div>

用于重用的高级类

如果我们有多个变量,并且需要监视它们,我们可以创建一个类,然后将其应用于我们的变量。我建议在change之前和change之后添加两个侦听器,这将使您在不同的过程中灵活地使用变量。

class ObservableObject { constructor(v) { this.v = v ?? 0 this.on = { beforeChange(newValue, oldValue) {}, afterChange(newValue, oldValue) {}, } } set value(newValue) { const oldValue = this.v // newValue, oldValue are the same if (oldValue === newValue) return this.on.beforeChange(newValue, oldValue) this.v = newValue this.on.afterChange(newValue, oldValue) } get value() { return this.v } } const $countABtn = document.getElementById('counter-a') const $countBBtn = document.getElementById('counter-b') const $outputA = document.getElementById('output-a') const $outputB = document.getElementById('output-b') const counterA = new ObservableObject() const counterB = new ObservableObject() const countOnClick = counter => { counter.value++ } const onChange = (v, output) => { output.textContent = v } $countABtn.onclick = () => { countOnClick(counterA) } $countBBtn.onclick = () => { countOnClick(counterB) } counterA.on.afterChange = v => { onChange(v, $outputA) } counterB.on.afterChange = v => { onChange(v, $outputB) } counterA.value = 50 counterB.value = 20 .wrapper { display: flex; flex-flow: row wrap; justify-content: center; align-items: center; width: 100vw } .item { width: 50% } .output { display: block; font-size: 2em; margin-top: 0.67em; margin-bottom: 0.67em; margin-left: 0; margin-right: 0; font-weight: bold; } <div class="wrapper"> <div class="item"> <button id="counter-a">Count A</button> <div id="output-a" class="output"></div> </div> <div class="item"> <button id="counter-b">Count B</button> <div id="output-b" class="output"></div> </div> </div>