如何侦听JavaScript中的变量更改?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (10)
  • 关注 (0)
  • 查看 (102)

在JS中是否可能有一个事件在某个变量的值发生变化时触发?

提问于
用户回答回答于

你需要一对带有某种类型的“addListener/emoveListener”接口的getter/setter...或者NPAPI插件

用户回答回答于

大多数浏览器(和IE6+)都有一个解决方案,它使用onPropertychange事件和新的规范定义Property。但问题是,你需要将变量设置为dom对象。

用户回答回答于

如果使用jQuery{UI},则可以用隐藏的方法改变<input/>元素。

用户回答回答于

AngularJS

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

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

用户回答回答于

建议使用两个get/set方法来访问你的值。

代码的一个问题是字段a物体的myobj是可直接访问的,因此可以访问它/更改其值,而不触发侦听器:

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所提出的监听器,但是你可以放心,没有任何可能的方式来读或写a未被注意的事情!

以编程方式添加封装字段

现在,我仍然建议一种简单的方法,通过简单的函数调用将封装的字段和相应的getter/setter添加到对象中。

请注意,这只会在value types.为了让这件事与你一起工作reference types,某种deep copy必须执行。

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);
用户回答回答于

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

// Console
function print(t) {
  var c = document.getElementById('console');
  c.innerHTML = c.innerHTML + '<br />' + t;
}

// Demo
var myVar = 123;

Object.defineProperty(this, 'varWatch', {
  get: function () { return myVar; },
  set: function (v) {
    myVar = v;
    print('Value changed! New value: ' + v);
  }
});

print(varWatch);
varWatch = 456;
print(varWatch);
<pre id="console">
</pre>

其他例子

// Console
function print(t) {
  var c = document.getElementById('console');
  c.innerHTML = c.innerHTML + '<br />' + t;
}

// Demo
var varw = (function (context) {
  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
print(varWatch);
varWatch = 456;
print(varWatch);

print('---');

varw('otherVarWatch', 123); // Declare with initial value
print(otherVarWatch);
otherVarWatch = 789;
print(otherVarWatch);
<pre id="console">
</pre>
用户回答回答于

这里有一个小手册给那些(像我一样)不明白Eli Gray的例子是如何工作的人:

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

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

你可以定义这样的对象,其中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;

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

用户回答回答于

如果它真的那么重要,那么你有两个选项(第一个是测试的,第二个不是):

首先,使用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);
}

第二,上面写着:

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);
用户回答回答于

扫码关注云+社区

领取腾讯云代金券