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
4 Answers
Reset to default 4var 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.