如何检测对象变量是否已更改?

7
我有一个代表围栏的类,它内部由矩形和圆形标记对象(也是我的类)组成。该围栏有4个变量 - x1、x2、y1和y2。如果其中任何一个更改了,我必须修改或重建内部标记对象。存储和检查这4个值并不是什么大问题,但这只是我的世界对象类中的第一个,以后可能会有更长的变量列表。是否有一种好的方法来检查是否更改了其中任何一个或在更改时触发某些操作,而不是显式地存储双重值并每次重绘画布时都进行检查?是否有类似于vb.net属性的东西?

你可以使用变量进行脏检查。 - rageit
你是指对象属性还是作用域变量? - Bergi
4个回答

4
var fence= {
   set x1(){
      alert('change');
      this.rebuild();
   },
   rebuild: function(){}
}

此外
function Fence(val){
    var value = val;

    this.__defineGetter__("x1", function(){
        return value;
    });

    this.__defineSetter__("x1", function(val){
        alert('change');
        this.rebuild();
    });
    this.rebuild = function(){};
}
var fence = new Fence();

抱歉,我之前匆忙处理了它。我的错。现在应该可以工作了。 - KJ Price
这是一篇关于 getters 和 setters 的好文章:http://ejohn.org/blog/javascript-getters-and-setters/ - KJ Price
哇,我之前完全不知道这个。谢谢你提供链接。 - Colin DeClue
是的,没问题。我真的很喜欢JavaScript发展的方向。 - KJ Price
__defineGetter____defineSetter__已经从标准中删除,因此它们可能会在未来的浏览器中失去支持。它们也不存在于IE 10及以下版本中。 - ckersch

1
使用下面代码中发布的对象,您可以轻松实现它:
function Fence() {
    // constructor
}

Fence.prototype.refresh = function() {
    // change the refresh code here
    console.log(this.x1 + "," + this.y1 + "," + this.x2 + "," + this.y2);
};

// must be called after the prototype.refresh function is defined
RefreshExtender.addRefreshProperties(Fence, [
        new RefreshExtender.Property("x1", 0), // propertyName, defaultValue[, refreshFunction]
        new RefreshExtender.Property("y1", 0, function() { console.log('Refresh only y1 property.'); }),
        new RefreshExtender.Property("x2", 0),
        new RefreshExtender.Property("y2", 0)
    ]);

然后,在使用它时:
var fence = new Fence();
fence.x1 = 20;
// Outputs: "20,0,0,0"

现在,如果您同时更改多个属性,它将在所有属性设置完成后才调用刷新函数。例如:
fence.x1 = 10; 
fence.x2 = 20;
// Outputs: "10,0,20,0 (Outputs only ONCE)"

如果我们更新y1属性,它将执行在创建属性时传递的函数:
fence.y1 = 30;
// Outputs: "Refresh only y1 property."

刷新扩展器:

var RefreshExtender = {
    addRefreshProperties: function(baseType, properties) {
        function defineProperty(property) {
            Object.defineProperty(baseType.prototype, property.name, {
                get: function() {
                    var val = this["_" + property.name];
                    if (typeof val === "undefined") {
                        return property.defaultValue;
                    }
                    return val;
                },
                set: function(val) {
                    var shouldRefresh = this["_" + property.name] !== val;
                    this["_" + property.name] = val;
                    if (shouldRefresh) {
                        if (typeof property.refreshFunction === "function") {
                            property.refreshFunction();
                        }
                        else {
                            this.refresh();
                        }
                    }
                },
                enumerable: true,
                configurable: true
            });
        }

        for (var i = 0, l = properties.length; i < l; i++) {
            defineProperty(properties[i]);
        }

        var oldRefreshFunction = baseType.prototype.refresh;

        baseType.prototype.refresh = RefreshExtender._executeOnce(oldRefreshFunction);
    },
    Property : function(name, defaultValue, refreshFunction) {
        this.name            = name;
        this.defaultValue    = defaultValue;
        if (typeof refreshFunction === "function") {
            this.refreshFunction = RefreshExtender._executeOnce(refreshFunction);
        }
    },
    _executeOnce : function(originalFunc) {
        var isRefreshing = false,
            func = function() {
                var _this = this;
                if (!isRefreshing) {
                    isRefreshing = true;
                    setTimeout(function() {
                        isRefreshing = false;
                        originalFunc.call(_this);
                    }, 0);
                }
            };

        return func;
    }
};

0
你可以创建一个闭包,其中包含访问setter和getter方法的权限,但没有直接访问属性的权限。类似这样:
var fence = function() {
    var x1, x2, y1, y2; 
    var setX1 = function(x) {
            if (typeof x == 'undefined') return x1;
            if (x != x1) alert('It Changed');
            x1 = x; 
    };
    var setX2 = function(x) {
            if (typeof x == 'undefined') return x2;
            if (x != x2) alert('It Changed');
            x2 = x; 
    };
    var setY1 = function(y) {
            if (typeof x == 'undefined') return y1;
            if (y != y1) alert('It Changed');
            y1 = y; 
    };
    var setY2 = function(y) { 
            if (typeof y == 'undefined') return y1;
            if (y != y2) alert('It Changed');
            y2 = y; 
    };
    return { x1: setX1, 
             x2: setX2, 
             y1: setY1, 
             y2: setY2 
           }
}()

fence.x1(1); //alerts "It changed"
fence.x1(1); //no alert

0
一个好的方法可能是定义一个栅栏的原型,而不是使用常规对象。对于原型,您可以创建setter方法而不是直接设置属性,并且让setter方法处理跟踪变量的更改。
原型应该看起来像这样:
function fence(x1, x2, x3, x4){
  this.x1 = x1;
  ....

  this.changedVars = {};
}

fence.Prototype.setX1 = function(newVal){
 this.x1 = newVal;
 this.changedVars.x1 = true;
};

这将在内部收集所有更改的变量。当您运行方法(比如“rebuildFence”)时,您将从this.changedVariables中删除更新的键。保留所有Setter方法(如果你愿意还可包括Getter方法)在原型上也可以减少内存开销,因为每次构建栅栏时都不必重新定义函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接