侦听JavaScript中的变量更改

侦听JavaScript中的变量更改,javascript,jquery,dom-events,Javascript,Jquery,Dom Events,在JS中是否可能有一个事件在某个变量的值更改时触发?JQuery被接受。不是直接接受的:您需要一个带有某种“addListener/RemovelListener”接口的pair getter/setter。。。或者一个NPAPI插件(但这完全是另一回事)。否 但是,如果它真的那么重要,您有两个选择(第一个是经过测试的,第二个不是): 首先,使用setter和getter,如下所示: var myobj = {a : 1}; function create_gets_sets(obj) { /

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

不是直接接受的:您需要一个带有某种“addListener/RemovelListener”接口的pair getter/setter。。。或者一个NPAPI插件(但这完全是另一回事)。

但是,如果它真的那么重要,您有两个选择(第一个是经过测试的,第二个不是):

首先,使用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);
正如(注意:这是指他的原始帖子;但在编辑之后,这里的要点仍然有效),我还建议使用一对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
。如何做到这一点?闭嘴

现在,您可以使用Luke建议的相同方法来创建和添加侦听器,但您可以放心,不可能不被注意地读取或写入
a

以编程方式添加封装字段 在Luke的轨道上,我现在提出一种简单的方法,通过一个简单的函数调用将封装的字段和相应的getter/setter添加到对象中

请注意,这仅适用于值类型。为了使用引用类型,必须实现某种深度复制(例如,请参见)

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

如何使用它?简单:

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()。

对于几年后进行的调整:

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

详情如下:


很抱歉提出一个老话题,但这里有一个小手册,供那些(像我一样!)不了解Eli Grey的例子是如何工作的人使用:

var test = new Object();
test.watch("elem", function(prop,oldval,newval){
    //Your code
    return newval;
});
希望这可以帮助某人(我知道这不是JQuery,但这可能会有所帮助。[纯JS理论上很好]):

其中“data”是范围中变量的名称


有一个

您正在寻找的功能可以通过使用“defineProperty()”方法来实现,该方法仅适用于现代浏览器:

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

一个小型jQuery扩展,用于处理对 变量、对象或键的存在。您可以分配任意数量的 回调可能受影响的任意数量的数据点 在后台运行的进程。jQueue侦听并等待 您指定要存在的这些数据,然后触发 使用其参数更正回调


使用
原型

//控制台
功能打印(t){
var c=document.getElementById('console');
c、 innerHTML=c.innerHTML+'
'+t; } //演示 var-myVar=123; Object.defineProperty(这是“varWatch”{ get:function(){return myVar;}, 设置:功能(v){ myVar=v; 打印('值已更改!新值:'+v); } }); 印刷(varWatch); varWatch=456; 印刷(varWatch)
一个相当简单的解决方案是只使用函数调用来设置全局变量的值,而不是直接设置其值。这样您就可以完全控制:

var globalVar;

function setGlobalVar(value) {
    globalVar = value;
    console.log("Value of globalVar set to: " + globalVar);
    //Whatever else
}
这是没有办法强制执行的,它只需要编程规则。。。尽管您可以使用
grep
(或类似工具)检查代码是否没有直接设置
globalVar
的值


或者您可以将其封装在对象和用户getter和setter方法中。。。只是一个想法。

是的,这现在完全可能了

我知道这是一个旧线程,但现在可以使用访问器(getter和setter)实现这种效果:

您可以这样定义一个对象,其中,
aInternal
表示字段
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;
请参见此处的示例:


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

这个问题最初是在2009年发布的,大多数现有答案要么过时、无效,要么需要包含大型臃肿的库:

  • 和都已弃用,不应使用
  • 是一个DOM元素事件处理程序,仅在某些IE版本中有效
  • 允许您使对象属性不可变,这将允许您检测尝试的更改,但也会阻止任何更改
  • 可以,但它需要大量的设置代码,并且在需要删除或创建新属性时无法正常工作
从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'
代理
对象的唯一缺点是:

  • 
    
    /* 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]);}
                        });
                }
    
    var globalVar;
    
    function setGlobalVar(value) {
        globalVar = value;
        console.log("Value of globalVar set to: " + globalVar);
        //Whatever else
    }
    
    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 = 42;
    
    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'
    
    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}}]
    
     
    
    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']);
    
    <button (click)="increment(1)">Increment</button>
    
    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);
    }
    
    <child-component (myEmitter)="monitorChanges($event)"></child-component>
    <br/>
    <label>{{n}}</label>
    
    public n: number = 0;
    
    public monitorChanges(n: number){
      this.n = n;
      console.log(n);
    }
    
    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;
      }
    }
    
    const money = new MonitoredVariable(0);
    
    console.log(money.val); // Get its value
    money.val = 2; // Set its value
    
    money.afterChange = (newValue, oldValue) => {
      console.log(`Money has been changed from ${oldValue} to ${newValue}`);
    };
    
    money.val = 3;
    
    Money has been changed from 2 to 3
    
    state.pageNumber = 0;
    // watch the console
    
    state.pageNumber = 15;
    // watch the console
    
    Object.defineProperty(window, 'player', {
      get: () => this._player,
      set: v => {
        console.log('window.player has been redefined!');
        this._player = v;
      }
    });
    
    var variableObject = {
        value: false,
        set: function (value) {
            this.value = value;
            this.getOnChange();
        }
    }
    
    variableObject.getOnChange = function() {
        if(this.value) {
            // do some stuff
        }
    }
    
    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);
    
    <!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>