How to check if an object has been modified - javascript

I have an object, for example:
var o = {
a: 1
};
The user does:
o.a = 2;
How can I know if the o object has been modified? I cannot touch the o object so I cannot use Object.defineProperties().

Since you are in the node.js environment and thus don't have to care about crappy old JavaScript engines (i.e. old browsers) you can use Object.defineProperty() to define properties with accessor functions. This allows you to execute a custom function whenever a certain property is read/written - so you can simply log the write and e.g. store it in a separate property.
var o = {};
Object.defineProperty(o, 'a', {
get: function() {
return this.__a;
},
set: function(value) {
this.__a = value;
this.__a_changed = true;
}
});
o.__a = 1;
Whenever a value is assigned to o.a the __a_changed property will be set. Of course it would be even cleaner to execute whatever you want to do on change right in the set function - but it obviously depends on your code if you can do so in a useful way.

The easiest thing would obviously be to just check if the value is different than what you initialized it as. Another option would be to use Object.defineProperty.
var o = {};
var aValue = 2;
Object.defineProperty(o, "a", {
get: function() { return aValue; },
set: function(newValue) {
aValue = newValue;
// trigger event or set flag that it was changed
}
});

You could always wrap o with your own setter getter if the defineProperty doesn't do the job for you.
var myO = {
o: o, // original o
changed: false,
set: function (prop, val) {
this.o[prop] = val;
this[prop + "IsChanged"] = true;
},
get: function (prop) {
return this.o[prop];
}
};
myO.set("a", 2);
console.log(myO.aIsChanged); // true
A better solution would be to execute an event using something like Backbone.Events to exeute someting myO.on("change", function () { ... })
var myO = {
o: o, // original o
changed: false,
set: function (prop, val) {
this.o[prop] = val;
this.trigger("change", prop, val);
},
get: function (prop) {
return this.o[prop];
}
};
_.extend(myO, Backbone.events);
myO.on("change", function (prop, val) {
// Do stuff with the change
});
myO.set("a", 1);
I'm using the Underscore library's extend method here, fyi.

var o_backup = o.a;
if(o.a != o_backup){
// function to execute
}
Basically, you are just creating a variable which will save the a from the object o and then you're checking if it has been modified or not.
Also, you can do a setInterval() function if you want to check if it has been modified more than once.

Related

JS defineProperty setter doesn't triggered

Q1: Can someone explain how to trigger setter in defineProperty, using it via function by this way?
Q2: How to get last key in setter?
fiddle is here
function test(root) {
Object.defineProperty(this, 'subtree', {
get: function() {
console.log("get");
return root.subtree;
},
set: function(value) { //doesn't triggered
console.log("set");
root.subtree = value;
}
});
}
var demo = new test({
subtree: {
state: null,
test: 1
}
});
console.log("START", demo.subtree);
demo.subtree.state = 13; // doesn't triggered setter, but change object
console.log("END", demo.subtree);
To make it simpler, this code
let variable = null;
let obj = {
set variable(value) {
variable = value;
}
get variable() {
return variable;
}
};
obj.variable = {a: 5};
console.log(obj.variable);
does exactly the same thing as this one
let variable = null;
let obj = {
setVariable(value) {
variable = value;
}
getVariable() {
return variable;
}
};
obj.setVariable({a: 5}); // equivalent to obj.variable = {a: 5}
console.log(obj.getVariable()); // equivalent to obj.variable
but the latter clearly shows what's going on.
We want to access a and set it to some value
console.log(obj.getVariable().a); // get a
obj.getVariable().a = 6; // set a!
Notice that we don't call setVariable to set a's value!!! This is exactly what happens in your code. You get subtree and set state to 13. To call setter, you do the following
obj.setVariable({a: 6});
obj.variable = {a: 6}; // getter/setter example
demo.subtree = {state: 13}; // your code
This and this (linked by you) present how scopes and capturing work, so you should get your hands on some book that covers all those things (or browse SO, there are (probably) plenty of questions about that).

Is it possible to assign a value to an object to trigger an action/method?

Example:
location = "http://www.mozilla.org";
You assign a string to a Location object to navigate to a new page.
Can it also be done with ordinary Javascript object?
The first method that comes to mind is setter, but it requires specifying the property name as Object.property.
The second method may be assignment overload, but it seems no such thing in Javascript.
Well you can use
Object.watch() property.
But as it is not supported in all browsers u need a
Polyfill to be added to your file.
Apart from these readymade functions you can write your own function using setTimeout() or window.requestAnimationFrame() where you can check the specific property of the function in consecutive threads.
Example :
var threadCheck = requestAnimationFrame ||
webkitRequestAnimationFrame ||
mozRequestAnimationFrame ||
oRequestAnimationFrame ||
msRequestAnimationFrame ||
function(callback) {
setTimeout(callback, 16);
},
oldProperty = {},
watchFunction = function (obj, objProp) {
var len = objProp.length,
prop,
i;
for (i= 0; i < len; i++) {
prop = objProp[i];
oldProperty[prop] = obj[prop];
}
checkFunction();
},
checkFunction = function () {
var prop;
for (prop in oldProperty) {
if (oldProperty[prop] !== obj[prop]) {
executeFunction();
oldProperty[prop] = obj[prop];
}
}
threadCheck(checkFunction);
},
executeFunction = function () {
console.log('changed')
},
obj = {
propertyA : function() {},
propertyB : 'check',
propertyC : {
dummy : 'xyz'
},
propertyD : 999
};
watchFunction(obj, ['propertyA', 'propertyB', 'propertyC', 'propertyD']);
Result(running the given code in the console of chrome browser, it's just a prototype, there are many scopes of improvements. Also unwatch() function has to be implemented):
obj.propertyA = function(){}
(){}
VM1016:35 changed
obj.propertyB = 'check'
"check"
obj.propertyB = 'check1'
"check1"
VM1016:35 changed
obj.propertyB = 'check1'
"check1"
obj.propertyB = 'check2'
"check2"
VM1016:35 changed
obj.propertyA = 'check2'
"check2"
VM1016:35 changed
obj.propertyA = function(){}
(){}
VM1016:35 changed
obj.propertyD = 999
999
obj.propertyD = 99
99
VM1016:35 changed
obj.propertyD = 991
991
VM1016:35 changed
Yes by defining a property.
var test = function (msg) {
alert(msg);
};
var d = Date.prototype;
Object.defineProperty(d, "year", {
get: function () { return this.getFullYear() },
set: function (y) { test(y); }
});
d.year = 2016;
→
Object.defineProperty(Window.prototype, "foo", {
get: function () { return 'hi' },
set: function (y) { alert('hi' + y); }
});
foo = 2016;
Stackoverflow - Javascript getters and setters for dummies?
Mozilla - Working with objects
Mozilla - Object.defineProperty()
No, it is not possible to assign something to an object and automatically trigger an action method in JavaScript. "location" is actually a property of window object. The getter of the location property returns an object "Location" that has "href" and some other properties and methods. The setter of the location property takes a string and set it internally to "href" property of "Location" object. You can implement something like this using JavaScript getter and setter yourself. See this for reference: http://javascriptplayground.com/blog/2013/12/es5-getters-setters/.
Hope this make sense.
Yes. ES5 introduced the concept of setters and getters to close the gap between APIs like window.location and element.innerHTML and what you can do in javascript.
Setters and getters can be defined directly in an object literal:
var myobj = {
set foo (str) {
// execute a function
},
get foo () {
// execute a function
}
}
Notice the syntax - there's no : after the keywords set and get to differentiate them from regular keys "set" and "get".
With the declaration above, doing either:
myobj.foo = somevalue;
or
somevar = myobj.foo;
will trigger the respective set and get functions.
Alternatively, if you already have an object, you may add a property with a setter and getter using Object. defineProperty():
Object.defineProperty(myobj, "bar", {
get: function () {
// execute a function
},
set: function (y) {
// execute a function
}
});
Now doing myobj.bar = x will call the setter function for bar.
See the following for more info:
https://developer.mozilla.org/my/docs/Web/JavaScript/Reference/Functions/set
https://developer.mozilla.org/my/docs/Web/JavaScript/Reference/Functions/get

Sync Objects to changed parameters

I want to bind the parameters of an Object to another Object, so they update whenever the other one updates aswell.
Object1 =
x: 1
Object2 =
x: Object1.x
y: 0
so that Object1.x = 2; updates Object2 aswell. (And the other way round)
How do I do that efficiently? I could use .watch and update the other one on each change, but I doubt that is a smart solution. Am I missing something?
Depending on the environment this has to run, a getter might be a suitable solution:
Object.defineProperty(Object2, 'x', {
get: function() {
return Object1.x;
},
enumerable: true
});
You might also want to define a setter to sync changes back.
You need to implement the observer/observable pattern.
I could use .watch and update the other one on each change, but I
doubt that is a smart solution
How you notify the other part if something changes? Why isn't a smart solution? It's just the solution!
Check this code listing I did to show you a possible implementation of objects capable of listening changes of other objects creating a base ObservableObject prototype (also available in JSFiddle!):
var ObservableObject = function () {
this._handlers = [];
this._disablePropertyChangeNotification = false;
};
ObservableObject.prototype = {
get disablePropertyChangeNotification() {
return this._disablePropertyChangeNotification;
},
set disablePropertyChangeNotification(value) {
this._disablePropertyChangeNotification = value;
},
listenPropertyChange: function (handler) {
this._handlers.push(handler);
},
notifyPropertyChange: function (propertyName) {
if (!this.disablePropertyChangeNotification) {
this._handlers.forEach(function (handler) {
handler(propertyName);
});
}
},
};
var A = function () {};
A.prototype = new ObservableObject();
Object.defineProperty(A.prototype, "name", {
get: function () {
return this._name;
},
set: function (value) {
this._name = value;
this.notifyPropertyChange("name");
}
});
var B = function () {};
B.prototype = new ObservableObject();
Object.defineProperty(B.prototype, "name", {
get: function () {
return this._name;
},
set: function (value) {
this._name = value;
this.notifyPropertyChange("name");
}
});
var someObjectA = new A();
var someObjectB = new B();
someObjectA.listenPropertyChange(function (propertyName) {
// This will prevent an infinite loop where
// property from A is set by B and viceversa
someObjectA.disablePropertyChangeNotification = true;
someObjectB[propertyName] = someObjectA[propertyName];
someObjectA.disablePropertyChangeNotification = false;
});
someObjectB.listenPropertyChange(function (propertyName) {
// This will prevent an infinite loop where
// property from A is set by B and viceversa
someObjectB.disablePropertyChangeNotification = true;
someObjectA[propertyName] = someObjectB[propertyName];
someObjectB.disablePropertyChangeNotification = false;
});
// We set name on A instance, and we print B instance name value
someObjectA.name = "hello world";
$(document.body).append("<p>someObjectB.name: " + someObjectB.name + "</p>");
// We set name on B instance, and we print A instance name value
someObjectB.name = "hello world changed";
$(document.body).append("<p>someObjectA.name: " + someObjectA.name + "</p>");

object.watch(), getting new value

In Firefox, I've got several objects that I need to trigger an event when a particular property of each is changed. I'm using object.watch(), however when I return the value of the property that was changed using "this", it returns the old value the first time, and "undefined" the second and subsequent times:
var myObject = {
"aProperty": 1
};
function propChanged(prop) {
alert(prop);
}
myObject.watch("aProperty", function () {
propChanged(this.aProperty);
});
myObject.aProperty = 2;//alerts "1"
myObject.aProperty = 3;//alerts "undefined"
The reason I can't just say alert(myObject.aProperty) is because this is meant to be a dynamic code that will apply the event handler to several, possibly unknown objects.
I'm just unsure exactly how to dynamically get the new value of the property using the watch method. I'm setting up a prototype for IE for this, so I'm not worried about it not working there. I just need to understand "this" and how it applies to the watch method's owner.
Edit>>
Here's the new code I'm using for cross browser, including the IE et al prototype:
var myObject = {};
if (!Object.prototype.watch) {
Object.prototype.watch = function (prop, handler) {
var oldval = this[prop], newval = oldval,
getter = function () {
return newval;
},
setter = function (val) {
oldval = newval;
return newval = handler.call(this, prop, oldval, val);
};
if (delete this[prop]) { // can't watch constants
if (Object.defineProperty) // ECMAScript 5
Object.defineProperty(this, prop, {
get: getter,
set: setter
});
else if (Object.prototype.__defineGetter__ && Object.prototype.__defineSetter__) { // legacy
Object.prototype.__defineGetter__.call(this, prop, getter);
Object.prototype.__defineSetter__.call(this, prop, setter);
}
}
};
}
if (!Object.prototype.unwatch) {
Object.prototype.unwatch = function (prop) {
var val = this[prop];
delete this[prop]; // remove accessors
this[prop] = val;
};
}
function propChanged(t, p, o, n) {
alert(o);
}
Object.defineProperty(myObject, "aProperty", {value: 2,
writable: true,
enumerable: true,
configurable: true});
myObject.watch("aProperty", propChanged);
myObject.aProperty = 3; //alerts 3
myObject.aProperty = 4; //alerts 4 (n is undefined in propChanged?
You need to return the value you want the property to have from the function you pass to watch.
myObject.watch("aProperty", function (prop, oldval, newval) {
propChanged(newVal);
return newVal;
});
should do it.
See the MDN docs for a full detail of the function but the relevant bit is
Watches for assignment to a property named prop in this object, calling handler(prop, oldval, newval) whenever prop is set and storing the return value in that property. A watchpoint can filter (or nullify) the value assignment, by returning a modified newval (or by returning oldval).
EDIT
Your edited code might work better this way
Object.prototype.watch = function (prop, handler) {
var fromPrototype = !Object.hasOwnProperty.call(this, prop),
val = this[prop],
getter = function () {
return fromPrototype ? Object.getPrototypeOf(this)[prop] : val;
},
setter = function (newval) {
fromPrototype = false;
return val = handler.call(this, prop, val, newval);
};
if (delete this[prop]) { // can't watch constants
if (Object.defineProperty) { // ECMAScript 5
Object.defineProperty(this, prop, {
get: getter,
set: setter,
configurable: true,
enumerable: true
});
} else if (Object.prototype.__defineGetter__ && Object.prototype.__defineSetter__) { // legacy
Object.prototype.__defineGetter__.call(this, prop, getter);
Object.prototype.__defineSetter__.call(this, prop, setter);
}
}
};

How do I compute a variable in JavaScript if and only if it is used?

This is what I'm doing right now.
var foo = function() {
var x = someComplicatedComputationThatMayTakeMoreTime();
this.foo = function() { return x; };
return x;
}
It works but only if foo is called as a function like so
foo();
But what if I want to call it as a normal variable with a value? I could modify the code to be
var foo = function() {
var x = someComplicatedComputationThatMayTakeMoreTime();
this.foo = x;
return x;
}
That would allow me to only call it once as a function and after that as a regular variable. But it's still not what I want. Plus it gets complicated if it accidentally gets called as a function again, returning an error.
Is this even possible in JavaScript?
BTW, this is for a Chrome/Firefox extension, so IE compatibility does not matter.
Ended up using toString because getters don't allow me to redefine the whole attribute, a function must be associated with it. And toString has cleaner syntax.
How about using toString?
var foo = function() {
function someComplicatedComputationThatMayTakeMoreTime() {
//your calculations
}
return {
toString: function() {
return someComplicatedComputationThatMayTakeMoreTime();
}
}
}
More about Object-to-Primitive Conversions in JavaScript
EDIT based on comment. Use a singleton (I think it's called):
myObject.prop = (function(){
function someComplicatedComputationThatMayTakeMoreTime() {
//your calculations
}
return {
toString: function() {
return someComplicatedComputationThatMayTakeMoreTime();
}
}
})()
If only Internet Explorer didn't exist, you could use getters and setters as described by John Resig in this blog article:
John Resig: JavaScript Getters and Setters
... They allow you to bind special functions to an object that look like normal object properties, but actually execute hidden functions instead.
Using a function is your best option for now, however the new JavaScript standard (ECMAScript 5th Ed.) which is being implemented now by all major browser vendors, gives you a method to create accessor properties, where you can define a property with a get and set functions that will be internally called, without worrying to treat this properties as functions, e.g.:
var obj = {};
Object.defineProperty(obj, 'foo', {
get: function () { // getter logic
return 'foo!';
},
set: function (value) {
// setter logic
}
});
obj.foo; // "foo!", no function call
This new standard will take some time to be implemented for all browsers, (the IE9 preview version really disappointed me), and I wouldn't recommend you to use it for production, unless you have total control on the environment where your application will be used.
What I think you want is a lazily instantiated variable, which can be implemented like this.
var myProperty = null;
function getMyProperty() {
return (myProperty = myProperty || builder());
}
This is not practical on the web because IE does not support it, but you can look at
https://developer.mozilla.org/en/defineGetter for examples how to do this.
There are a couple ways to do it, here is one example:
var data = {};
data.__defineGetter__("prop",
(function () {
var value = null;
return function () {
if (null == value) {
value = getYourValueHere();
}
return value;
};
})());
and now you can use it like:
var a = data.prop;
var b = data.prop;
I would recommend a variation on ChaosPandion's answer, but with a closure.
var myProperty = (function () {
var innerProperty = null;
return function() {
return (innerProperty = innerProperty || someComplicatedComputationThatMayTakeMoreTime());
};
})();
and then use myProperty() every time you need to access the variable.
You could define a JavaScript getter. From the Apple JavaScript Coding Guidelines:
myObject.__defineGetter__( "myGetter", function() { return this.myVariable; } );
var someVariable = myObject.myGetter;
See John Resig's post, JavaScript Getters and Setters, and the Defining Getters and Setters page at the Mozilla Developer Centre for more information.
I would use explicit lazy evaluation. Here's my implementation of it based on Scheme's take:
var delay, lazy, force, promise, promiseForced, promiseRunning;
(function () {
var getValue = function () {
return this.value;
};
var RUNNING = {};
var DelayThunk = function (nullaryFunc) {
this.value = nullaryFunc;
};
DelayThunk.prototype.toString = function () {
return "[object Promise]";
};
DelayThunk.prototype.force = function () {
if (promiseRunning (this)) {
throw new Error ("Circular forcing of a promise.");
}
var nullaryFunc = this.value;
this.value = RUNNING;
this.value = nullaryFunc ();
this.force = getValue;
return this.value;
};
var LazyThunk = function (nullaryFunc) {
DelayThunk.call (this, nullaryFunc);
};
LazyThunk.prototype = new DelayThunk (null);
LazyThunk.prototype.constructor = LazyThunk;
LazyThunk.prototype.force = function () {
var result = DelayThunk.prototype.force.call (this);
while (result instanceof LazyThunk) {
result = DelayThunk.prototype.force.call (result);
}
return force (result);
};
delay = function (nullaryFunc) {
return new DelayThunk (nullaryFunc);
};
lazy = function (nullaryFunc) {
return new LazyThunk (nullaryFunc);
};
force = function (expr) {
if (promise (expr)) {
return expr.force ();
}
return expr;
};
promise = function (expr) {
return expr instanceof DelayThunk;
};
promiseForced = function (expr) {
return expr.force === getValue || !promise (expr);
};
promiseRunning = function (expr) {
return expr.value === RUNNING || !promise (expr);
};
}) ();
Example Syntax:
var x = lazy (function () { return expression; });
var y = force (x);
var z = delay (function () { return expression; });
var w = force (z);
Note values are stored once evaluated, so repeated forcing will not do extra computations.
Example usage:
function makeThunk (x, y, z) {
return lazy (function () {
// lots of work done here
});
}
var thunk = makeThunk (arg1, arg2, arg3);
if (condition) {
output (force (thunk));
output (force (thunk)); // no extra work done; no extra side effects either
}
You can use the javascript Proxy class for creating such functionality.
var object = {};
var handler = {
resolvers: {},
get ( target, property, proxy ) {
if ( ! target.hasOwnProperty( property ) && this.resolvers.hasOwnProperty( property ) ) {
// execute the getter for the property;
target[ property ] = this.resolvers[ property ]();
}
return target[ property ];
},
set ( target, property, value, receiver ) {
// if the value is function set as a resolver
if ( typeof value === 'function' ) {
this.resolvers[property] = value;
// otherwise set value to target
} else {
target.property = value;
}
},
has ( target, property, receiver ) {
//true when proxy handler has either a resolver or target has a value;
return this.resolvers.hasOwnProperty( property ) || target.hasOwnProperty( property );
}
};
var lazyObject = new Proxy( object, handler );
Now you can use it like this:
'exampleField' in lazyObject; //returns false
lazyObject.exampleField = function(){ return 'my value' }; // add a resolver function
'exampleField' in lazyObject; //returns true
lazyObject.exampleField; //executes your resolver function and returns 'my value'
This example is to demonstrate the working. You can change after your needs.
Here is a fiddle with a demonstration

Categories

Resources