最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to detect if an object variable has been changed? - Stack Overflow

programmeradmin0浏览0评论

I have a class that represents a fence, internally its made up from rectangle and circle marker objects (also my classes). The fence has 4 variables - x1, x2, y1 and y2. If any of these changes I have to modify or rebuild the internal marker objects.

Storing and checking the 4 values isn't such a big deal but this is just the first of my world object classes and there will be ones with much longer lists of variables. Is there any good way of checking whether any of these has changed or trigger something on change without explicitly storing double values and checking each time the canvas is redrawn? Something like a property in vb or such?

I have a class that represents a fence, internally its made up from rectangle and circle marker objects (also my classes). The fence has 4 variables - x1, x2, y1 and y2. If any of these changes I have to modify or rebuild the internal marker objects.

Storing and checking the 4 values isn't such a big deal but this is just the first of my world object classes and there will be ones with much longer lists of variables. Is there any good way of checking whether any of these has changed or trigger something on change without explicitly storing double values and checking each time the canvas is redrawn? Something like a property in vb or such?

Share Improve this question asked Jul 14, 2014 at 20:30 Jake FreelanderJake Freelander 1,4712 gold badges21 silver badges29 bronze badges 2
  • You can use a variable to do dirty checking. – rageit Commented Jul 14, 2014 at 20:33
  • Do you mean an object property or a scope variable? – Bergi Commented Jul 14, 2014 at 20:41
Add a ment  | 

4 Answers 4

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

Also

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

Using the object posted in the code below, you can achieve it quite easily:

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)
    ]);

Then when using it:

var fence = new Fence();
fence.x1 = 20;
// Outputs: "20,0,0,0"

Now if you change multiple properties at once, it will only call the refresh function after all the properties have been set. For example:

fence.x1 = 10; 
fence.x2 = 20;
// Outputs: "10,0,20,0 (Outputs only ONCE)"

If we update the y1 property, it will execute the function passed in when creating the property:

fence.y1 = 30;
// Outputs: "Refresh only y1 property."

Refresh Extender:

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;
    }
};

You could create a closure with access to setter and getter methods, but no direct access to the properties. Something like:

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

A good approach may be to define a prototype for a fence rather than using a regular object. For a prototype, you can create setter methods rather than directly setting an attribute, and have the setter methods handle tracking changes to variables as well.

The prototype would look something like this:

function fence(x1, x2, x3, x4){
  this.x1 = x1;
  ....

  this.changedVars = {};
}

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

This would collect all changed variables internally. When you ran a method (say, 'rebuildFence'), you would delete the keys you updated from this.changedVariables. Keeping all of the setter methods (and getter methods, if you are so inclined), on the prototype will also reduce memory overhead, since you won't be redefining the functions every time you build a fence.

发布评论

评论列表(0)

  1. 暂无评论