As others have noted, you can use the [Proxy][6] object to monitor (and intercept) changes made to an object. 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'
**However, what OP is trying to do is be notified of any mutation on a broader state object.** To do that, and use `Proxy`, you need an observer that will instrument the entire object graph. TJCrowder correctly noted that doing so is a non-trivial effort. You'll need to use a specialized library such as **[Observable Slim][8]** (which I have authored). This library does use `Proxy` under the hood, but handles all the complexity of performantly instrumenting the full object graph.
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}}]
[6]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
[8]: https://github.com/ElliotNB/observable-slim
It's now 2018 and the answers to this question are a bit outdated:
- [Object.watch][1] and [Object.observe][2] are both deprecated and should not be used.
- [onPropertyChange][3] is a DOM element event handler that only works in some versions of IE.
- [Object.defineProperty][4] 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][5] 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][6] 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:
1. The `Proxy` object is not available in older browsers (such as IE11) and the [polyfill][7] cannot fully replicate `Proxy` functionality.
2. Proxy objects do not always behave as expected with special objects (e.g., `Date`) -- the `Proxy` 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][8]** (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}}]
[1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/watch
[2]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/observe
[3]: https://msdn.microsoft.com/en-us/ie/ms536956(v=vs.94)
[4]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
[5]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#Defining_getters_and_setters
[6]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
[7]: https://github.com/GoogleChrome/proxy-polyfill
[8]: https://github.com/ElliotNB/observable-slim