<div dir="ltr"><div class="gmail_default"><div class="gmail_default"><font color="#000000" face="georgia, serif">> I don't think there's any solution other than diffing</font></div><div class="gmail_default"><font color="#000000" face="georgia, serif"><br></font></div><div class="gmail_default"><font color="#000000" face="georgia, serif">And how would you diff without polling (while supporting IE)?</font></div><div class="gmail_default"><font color="#000000" face="georgia, serif"><br></font></div><div class="gmail_default"><font color="#000000" face="georgia, serif"><div class="gmail_default">`Proxy` is powerful, but it's not as good as `Object.observe` would've been for some very simple tasks.</div><div class="gmail_default"><br></div><div class="gmail_default">Every time I wish I could use `Proxy` in a simple way, there's always some issue with it. For example: <a href="https://jsfiddle.net/trusktr/hwfontLc/17">https://jsfiddle.net/trusktr/hwfontLc/17</a></div><div class="gmail_default"><br></div><div class="gmail_default">Why do I have to sacrifice the convenience of writing ES6 classes just to make that work? And plus that introduced an infinite recursion that I overlooked because I didn't treat the get/set the same way as we should treat getters/setters and store the value in a different place. It's just more complicated than `Object.observe`.</div><div class="gmail_default"><br></div><div class="gmail_default">If we want to use ES6 classes, we have to come up with some convoluted pattern for returning a Proxied object from a constructor possibly deep in a class hierarchy, so that all child classes can use the proxied `this`.</div><div class="gmail_default"><br></div><div class="gmail_default">Using `Proxy` like this is simply not ideal compared to `Object.observe`.</div><div class="gmail_default"><br></div><div class="gmail_default">> not exactly the same as Object.observe</div><div class="gmail_default"><br></div><div class="gmail_default">Yep :)</div><div class="gmail_default"><br></div><div class="gmail_default">> When you diff is totally up to your use case</div><div class="gmail_default"><br></div><div class="gmail_default">I'd like performant change notifications without interfering with object structure (f.e. modifying descriptors) or without interfering with the way people write code. I want to have synchronous updates, because that gives me the ability to opt-in to deferring updates. If the API is already deferred (f.e. polling like in the official and deprecated Object.observed polyfill), then there's not a way to opt-in to synchronous updates.<br><br></div><div class="gmail_default"><div class="gmail_default">I simply would like to observe an object with a simple API like:</div><div class="gmail_default"><br></div><div class="gmail_default">```js</div><div class="gmail_default">import someObject from 'any-npm-package-that-could-ever-exist'</div><div class="gmail_default"><br></div><div class="gmail_default">const thePropsIWantToObserve = ['foo', 'bar', 'baz']</div><div class="gmail_default"><br></div><div class="gmail_default">Object.observeProps( someObject, thePropsIWantToObserve, (name, oldValue, newValue) => {</div><div class="gmail_default">  console.log('property on someObject changed:', name, oldValue, newValue)</div><div class="gmail_default">})</div><div class="gmail_default">```</div><div class="gmail_default"><br></div><div class="gmail_default">I'd be fine if it only gave me two args, `name` and `newValue`, and I could optionally cache the oldValue myself if I really wanted to, which automatically saves resources by making that opt-in. I'd also want it to be at the very least triggering observations on a microtask. Synchronous would be better, so I can opt-in to deferring myself. Maybe and option can be passed in to make it synchronous.</div><div class="gmail_default"><br></div><div class="gmail_default">---</div><div class="gmail_default"><br></div><div class="gmail_default"><div class="gmail_default">I won't shoot myself in the foot with `Object.observe`. I know what I plan to do with the gun, if it ever comes to exist. <span style="font-size:small;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">If one builds a tank (an API) and places a user in it, that user can't shoot themselves in the foot, can they?<span> </span></span><span style="font-size:small;text-decoration-style:initial;text-decoration-color:initial;background-color:rgb(255,255,255);float:none;display:inline">(I'm anti-war pro-peace and against violence, that's just an analogy.) <span style="text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">It's like a drill: sure, some people aren't very careful when they use drills the wrong way and hurt themselves? What about the people who know how to use the drills? Maybe we're not considering those people when deciding that drills should be outlawed because one person hurt themselves with one. </span></span></div><div class="gmail_default"><span style="font-size:small;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline"><br></span></div><div class="gmail_default"><span style="font-size:small;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">Let's let people who know what they're doing make good use of the tool. A careless programmer will still shoot themselves in the foot even without Object.observe. There's plenty of ways to do it as is.</span></div><div class="gmail_default"><br></div><div class="gmail_default"><div class="gmail_default">If someone can currently implement `Object.observe` by using polling with diffing, or by hacking getter/setter descriptors, why not just let them have the legitimate native implementation? <span style="font-size:small;background-color:rgb(255,255,255);text-decoration-style:initial;text-decoration-color:initial;float:none;display:inline">For people who are going to shoot their foot off anyways, let's let them at least impale their foot efficiently instead of using a spoon, while the professionals can benefit from the tool.</span></div><div class="gmail_default"><br></div><div class="gmail_default">We've got libs like Backbone.js that make us write things like `someObject.set('foo', 123)` so that we can have the same thing as `Object.observe` provides. Backbone was big. This shows that there's people that know how to use the pattern correctly. This is another example of a library author having to tell end users to write code differently in order to achieve the same goal as we'd simply have with `Object.observe`:</div><div class="gmail_default">ideally we'd only need to write `someObject.foo = 123` which saves both the author of `someObject` and the consumer of `someObject` time.</div><div class="gmail_default"><br></div><div class="gmail_default">It'd simply be so nice to have `Object.observe` (and preferably a simpler version, like my following example).</div><div class="gmail_default"><br></div><div class="gmail_default">So for my use case, I'll use the following small implementation. You may notice it has many caveats that are otherwise non-existent with `Object.observe` like,</div><div class="gmail_default"><br></div><div class="gmail_default">1. It doesn't consider inherited getters/setters.</div><div class="gmail_default">2. It doesn't consider that `isObserved` can be deleted if someone else sets a new descriptor on top of the observed descriptor.</div><div class="gmail_default">3. It may trigger unwanted extra side-effects by call getters more than once.</div><div class="gmail_default">4. etc.</div><div class="gmail_default"><br></div><div class="gmail_default">`Object.observe` simply has not problems (in theory, because the implementation which is on the native side does not interfere with the interface on the JavaScript side)!</div><div class="gmail_default"><br></div><div class="gmail_default">So the following is what I'm using, which works in my specific use cases where the above caveats are not a problem:</div><div class="gmail_default"><br></div><div class="gmail_default">```js</div><div class="gmail_default"><div class="gmail_default">const isObserved = Symbol()</div><div class="gmail_default"><br></div><div class="gmail_default">function observe(object, propertyNames, callback) {</div><div class="gmail_default">    let map</div><div class="gmail_default"><br></div><div class="gmail_default">    for (const propName of propertyNames) {</div><div class="gmail_default">        const descriptor = Object.getOwnPropertyDescriptor(object, propName) || {}</div><div class="gmail_default"><br></div><div class="gmail_default">        if (descriptor[isObserved]) continue</div><div class="gmail_default"><br></div><div class="gmail_default">        let getValue</div><div class="gmail_default">        let setValue</div><div class="gmail_default"><br></div><div class="gmail_default">        if (descriptor.get || descriptor.set) {</div><div class="gmail_default">            // we will use the existing getter/setter assuming they don't do</div><div class="gmail_default">            // anyting crazy that we might not expect. (See? Another reason for</div><div class="gmail_default">            // Object.observe)</div><div class="gmail_default">            const oldGet = descriptor.get</div><div class="gmail_default">            const oldSet = descriptor.set</div><div class="gmail_default"><br></div><div class="gmail_default">            getValue = () => oldGet.call(object)</div><div class="gmail_default">            setValue = value => oldSet.call(object, value)</div><div class="gmail_default">        }</div><div class="gmail_default">        else {</div><div class="gmail_default">            if (!map) map = new Map</div><div class="gmail_default"><br></div><div class="gmail_default">            const initialValue = descriptor.value</div><div class="gmail_default">            map.set(propName, initialValue)</div><div class="gmail_default"><br></div><div class="gmail_default">            delete descriptor.value</div><div class="gmail_default">            delete descriptor.writable</div><div class="gmail_default"><br></div><div class="gmail_default">            getValue = () => map.get(propName)</div><div class="gmail_default">            setValue = value => map.set(propName, value)</div><div class="gmail_default">        }</div><div class="gmail_default"><br></div><div class="gmail_default">        Object.defineProperty(object, propName, {</div><div class="gmail_default">            ...descriptor,</div><div class="gmail_default"><br></div><div class="gmail_default">            get() {</div><div class="gmail_default">                return getValue()</div><div class="gmail_default">            },</div><div class="gmail_default"><br></div><div class="gmail_default">            set(value) {</div><div class="gmail_default">                setValue(value)</div><div class="gmail_default">                callback(propName, getValue())</div><div class="gmail_default">            },</div><div class="gmail_default"><br></div><div class="gmail_default">            [isObserved]: true,</div><div class="gmail_default">        })</div><div class="gmail_default">    }</div><div class="gmail_default">}</div></div><div class="gmail_default">```</div><div class="gmail_default"><br></div><div class="gmail_default">And the usage looks like:</div><div class="gmail_default"><br></div><div class="gmail_default">```js</div><div class="gmail_default"><div class="gmail_default">const o = {</div><div class="gmail_default">    foo: 1,</div><div class="gmail_default">    bar: 2,</div><div class="gmail_default">    get baz() {</div><div class="gmail_default">        console.log('original get baz')</div><div class="gmail_default">        return this._baz</div><div class="gmail_default">    },</div><div class="gmail_default">    set baz(v) {</div><div class="gmail_default">        console.log('original set baz')</div><div class="gmail_default">        this._baz = v</div><div class="gmail_default">    },</div><div class="gmail_default">}</div><div class="gmail_default"><br></div><div class="gmail_default">observe(o, ['foo', 'bar', 'baz'], (propName, newValue) => {</div><div class="gmail_default">    console.log('changed value:', propName, newValue)</div><div class="gmail_default">})</div><div class="gmail_default"><br></div><div class="gmail_default">o.foo = 'foo'</div><div class="gmail_default">o.bar = 'bar'</div><div class="gmail_default">o.baz = 'baz'</div></div><div class="gmail_default">```</div></div></div></div></font></div><div class="gmail_default"><font color="#000000" face="georgia, serif"><br></font></div></div><div><div dir="ltr" class="gmail_signature"><div dir="ltr"><b style="font-size:12.8px">/#<i>!</i>/</b><font face="courier new, monospace" style="font-size:12.8px">JoePea</font><br></div></div></div><br></div>