A Proxy based implementation of Object.observe
JavaScript HTML
Latest commit ac1a439 Jan 21, 2017 @anywhichway Fixed issue #16.

README.md

proxy-observe

Object.deepObserve goes beyond the EcmaScript spec and implements the ability to observe an object and all its sub-objects with a single call.

Build Status Codacy Badge Code Climate Test Coverage Issue Count

NPM

A Proxy Based Implementation Of Object.observe, Array.observe plus Object.deepObserve. Object.observe and Array.observe have now been deprecated from Chrome and standards tracks, but some developers may still find them useful or require them for backward compatibility in Chrome applications.

Installation

npm install proxy-observe

The index.js and package.json files are compatible with https://github.com/anywhichway/node-require so that proxy-observe can be served directly to the browser from the node-modules/proxy-observe directory when using node Express. You can also use the files in the browser subdirectory directly.

In late April 2016 Object.observe disappeared from Chrome and Proxy appeared. As a result, the focus on supporting this library will go up so that can be used to replace what is now missing functionality in Chrome as well as support observers in other browsers.

Philosophy

This library exists because despite some people's reasonable philosophical concerns regarding the use of observers and the fact they have been deprecated from Chrome and were never fully supported in other JavaScript engines, some people still want to use them.

There is a slightly more complete EcmaScript implementation available at https://github.com/MaxArt2501/object-observe. This is probably the most popular Object.observe polyfill available at the moment. However, it has well documented and acknowledged shortcomings. It is based on polling which means some events get delivered out of order or even missed. It's author provides a reasonable rationale for not using Proxies to implement Object.observe.

The above being said, we had an application that could not afford to miss events or have them out of order and we also wanted something lighter and potentially faster. Hence, we built a Proxy based polyfill. As far as we know the sole shortcomings of our implementation are:

  1. There are some less used functions not yet implemented, e.g. deliverChangeRecords.

  2. The variables pointing to objects that are being observed must be re-assigned to point to a proxy returned by the call to Object.observe, e.g.

var object = { foo: null };
object = Object.observe(object, function(changeset) {
    console.log(changeset)
});
object.foo = "bar";

will result in {foo: "bar"} being printed to the console

Item one above can be re-mediated over time.

We believe item two is a small price to pay. Our implementation is also less than 200 lines of code and 4.5K minified including Array.observe and Object.deepObserve vs. over 500 lines of code and 28K for MaxArt2501 covering just Object.observe.

There is an additional implementation at https://github.com/joelgriffith/object-observe-es5. This implementation is synchronous and modifies all object properties to have custom getters and setters. This could really bind up your application if there are a lot of changes to objects. It also only monitors enumerable properties and like the MaxArt2501 implementation is several hundred lines of code.

Anyway, now you have a choice MaxArt2501, Joel Griffith or AnyWhichWay, and choice is good! They all have their pros and cons.

Usage and Enhancements

Object.observe = function(object,[callback[,acceptlist[,pausable[,pause[,delay]]])

Proxy-observe makes the second arguments to Object.unobserve and Array.unobserve optional so that an object can be completely un-observed in one call. Additionally, <instance>.unobserve returns the original observed object or array "deproxied".

Object.observe

Proxy-observer supports a deepObserve capability on nested objects. It also allows the pausing and starting of observers using two additional optional arguments to Object.observe.

pausable - a boolean that indicates to create the observer so it can be paused. The argument pausable is optional to reduce the chance of shadowing a property or method on any existing code.

pause - a boolean that indicates to create the observer in paused state

If pausable is true then an additional method deliver(ignorePrevious) is available to start delivery.

To pause delivery, set a property called pause on the function deliver to true. Re-set it to false and call deliver(ignorePrevious) to re-start change handling. If ignorePrevious is set to true, then queued changes will not cause the invocation of observer callbacks.

What's Implemented and Not and Other Issues

Array.observe does not behave well in Chrome. It is the native implementation that does not behave well. We are looking to patch this in the future. Unit tests pass in Firefox, which uses our observe implementation.

You can observe for ["add", "update", "delete", "reconfigure", "setPrototype","preventExtensions"] using Object.observe.

You can observe for ["add", "update", "delete", "splice"] using Array.observe.

Object.getNotifier and Object.deliverChangeRecords are not implemented.

Currently Object.deepObserve does not support event type selectivity. All events are monitored. There is also no Object.deepUnobserve.

v0.0.12 setPrototypeOf observing does not work in Firefox.

Release History

v0.0.20 2017-01-21 Fixed issue #16. Thanks @vitormsilva!

v0.0.19 2016-11-13 Fixed issue 15 where "delay" was undefined.

v0.0.18 2016-07-22 Ehanced to take a 6th argument, delay, to set delivery timeout on changes in milliseconds. Also reduced CPU load by making the delivery function "smart". Delivery no longer creates timeout calls if there is nothing to deliver. The next change to an object will restart the delivery function with the previous delay setting.

v0.0.17 2016-06-15 Fixed Issue 10 thanks to goodells. Updated unit test should support response to pop accordingly.

v0.0.16 2016-06-02 Modified delivery timeout from 0ms to 10ms to reduce CPU loading.

v0.0.15 2016-05-15 README updates. Unobserve added to Array corrected issue with Object.unobserve not working. Also made callback argument to unobserve optional, which eliminates all observations. Finally, added an unobserve method on on observed instances which returns the original object, i.e. de-proxies, Added more unit tests.

v0.0.13,14 2016-05-12 README and code style improvements.

v0.0.12 2016-05-11 Addressed issue 5. Added unit tests. Coverage over 90%. Array.unobserve still not implemented. setPrototypeOf observing does not work in Firefox.

v0.0.11 2016-05-06 Addressed issues 2,3,4 ... added some unit tests to address issue 4. Unsure how long issue 2 has been undiscovered. Issue 3 has been in all versions. Issue 4 is an enhancement. Started testing in NodeJS v6.0 now that Proxy is supported by NodeJS. Array.unobserve still not implemented.

v0.0.10 2016-01-26 Added browserified versions. Re-wrote unit tests to use blanket.js since unit tests will always pass on Chrome since it has native support for Object.observe and unit testing must be done in the browser until node.js support MS Chakra. However, the test coverage reporting is not working.

v0.0.9 2015-12-13 Added some unit tests and updated documentation. Consider this a BETA.

v0.0.8 2015-12-13 Codacy driven enhancements. Consider this a BETA.

v0.0.6 2015-11-07 Fixed another issue with testing for existence of Proxy object. Consider this a BETA.

v0.0.5 2015-11-07 Fixed issue with testing for existence of Proxy object. Consider this a BETA.

v0.0.4 2015-10-03 Fixed issue with deepObserve discovered during code walkthrough. Issue would have impacted non-Chrome browsers. Consider this a BETA.

v0.0.3 2015-10-01 Updated README. No unit tests yet. Consider this a BETA.

v0.0.2 2015-10-01 Added Object.deepObserve and Array.observe. No unit tests yet. Consider this a BETA.

v0.0.1 2015-10-01 Initial release. No unit tests yet. Consider this a BETA.

License

MIT License - see LICENSE file