Listener for property value changes in a Javascript object
What watch is really designed for is validation of property values. For example you could validate that something is an integer:
obj.watch('count', function(id, oldval, newval) {
var val = parseInt(newval, 10);
if(isNaN(val)) return oldval;
return val;
});
You could use it to validate string length:
obj.watch('name', function(id, oldval, newval) {
return newval.substr(0, 20);
});
However, these are only available in the latest versions of the SpiderMonkey javascript engine. Great if you are using Jaxer or embedding the SpiderMonkey engine, but not really available in your browser yet (unless you are using FF3).
You could take a look at the Javascript Propery Events library. It's a small library extending Object.defineProperty
with some event callers, that I made recently. It adds a few on[event]
properties that can be used like the on[event]
properties of HTML-Objects. It also has a simple type check, which calls the onerror
event if it fails.
Taking your code it would result in something like this:
var o = {}
Object.defineProperty(o, "p", {
value:1,
writable:true,
onchange:function(e){
console.log("o." + e.target + " changed from " + e.previousValue + " to " + e.returnValue);
}
})
It's now 2018 and the answers to this question are a bit outdated:
- Object.watch and Object.observe are both deprecated and should not be used.
- onPropertyChange is a DOM element event handler that only works in some versions of IE.
- Object.defineProperty allows you to make an object property immutable, which would allow you to detect attempted changes, but it would also block any changes.
- Defining setters and getters works, but it requires a lot of setup code and it does not work well when you need to delete or create new properties.
Today, you can now use the Proxy object to monitor (and intercept) changes made to an object. It is purpose built for what the OP is trying to do. Here's a basic example:
var targetObj = {};
var targetProxy = new Proxy(targetObj, {
set: function (target, key, value) {
console.log(`${key} set to ${value}`);
target[key] = value;
return true;
}
});
targetProxy.hello_world = "test"; // console: 'hello_world set to test'
The only drawbacks of the Proxy
object are:
- The
Proxy
object is not available in older browsers (such as IE11) and the polyfill cannot fully replicateProxy
functionality. - Proxy objects do not always behave as expected with special objects (e.g.,
Date
) -- theProxy
object is best paired with plain Objects or Arrays.
If you need to observe changes made to a nested object, then you need to use a specialized library such as Observable Slim (which I authored). It works like this:
var test = {testing:{}};
var p = ObservableSlim.create(test, true, function(changes) {
console.log(JSON.stringify(changes));
});
p.testing.blah = 42; // console: [{"type":"add","target":{"blah":42},"property":"blah","newValue":42,"currentPath":"testing.blah",jsonPointer:"/testing/blah","proxy":{"blah":42}}]
Check out Object.defineProperty
and
Object.prototype.\__defineGetter__
(or \__defineSetter__
) to see where this functionality is heading.
Object.defineProperty
should be available in all contemporary browsers real soon now.