Polyfilling Object.observe

classic Classic list List threaded Threaded
11 messages Options
Reply | Threaded
Open this post in threaded view
|

Polyfilling Object.observe

/#!/JoePea
Is there a way to polyfill `Object.observe` in such a way that the object before observation is the same reference as the object being observed after the call (i.e. not a Proxy), and other than monkey-patching getters/setters?

Is defining getters/setters the only way?

/#!/JoePea

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Polyfilling Object.observe

Ranando King
The only way I can think of that might work will only work if all actions against the observed object happen through methods of the observed object's prototype. In that case, you can proxy the prototype. Short of that, you're out of luck.

On Tue, Jul 24, 2018 at 12:02 PM /#!/JoePea <[hidden email]> wrote:
Is there a way to polyfill `Object.observe` in such a way that the object before observation is the same reference as the object being observed after the call (i.e. not a Proxy), and other than monkey-patching getters/setters?

Is defining getters/setters the only way?

/#!/JoePea
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Polyfilling Object.observe

T.J. Crowder-2
In reply to this post by /#!/JoePea
On Tue, Jul 24, 2018 at 6:01 PM, /#!/JoePea
<[hidden email]> wrote:
> Is there a way to polyfill `Object.observe` in such a way that the object
> before observation is the same reference as the object being observed after
> the call (i.e. not a Proxy), and other than monkey-patching getters/setters?
>
> Is defining getters/setters the only way?

Even that doesn't really polyfill it, because `Object.observe` got notifications of changes when new properties were created as well.

But yes, **if** you know the names of the properties in advance (the "shape" of the object, I believe, is the current parlance?), and if you want notifications of changes just to those properties, I think monkeypatching will be your simplest and most successful approach.

If you need to catch additions as well, I don't think there's any solution other than diffing, which is quite yucky.

-- T.J. Crowder

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Polyfilling Object.observe

/#!/JoePea
But yes, **if** you know the names of the properties in advance

I don't because I'm using [`element-behaviors`](https://github.com/trusktr/element-behaviors) (which I wrote). Behaviors can be arbitrarily added and removed from an element, behaviors can observe any arbitrary attributes on an element, but the difficulty is in behaviors observing arbitrary properties on an element regardless of if the props already exist. Because an element may have any number of unknown behaviors added to it in the future, there's no way to pre-meditate the set of props that will be observed.

The only way I can think of that might work will only work if all actions against the observed object happen through methods of the observed object's prototype

If I drop support for IE, maybe Proxy will help me.

The thing is, what are the implications? It seems a bit tricky to introduce a Proxy somewhere in the middle of a class hierarchy.

- How do I return a Proxied this from a class in the middle of a hierarchy without changing patterns? Seems like I could use a single base `constructor` then move all construction logic to a `construct` method called by the base `constructor`, to make things easier. I do a similar hack now anyways in order to make ES5-style classes work with native Custom Elements.
- How do we proxify a class prototype when using ES6 classes? Do we just `SomeClass.prototype = new Proxy(SomeClass.prototype, handler)`? Any implications of that?
- Seems like `Object.observe` would've just been the easiest way to achieve what I want, if that hadn't been dropped.
- I currently use `MutationObserver` to observe HTML element attribute changes, which is the like the equivalent of `Object.observe` for DOM. But the downside of this is that it only covers attributes, not instance properties. Plus, attributes are always strings, incurring performance overhead. And all the new frameworks delegate to instance properties, bypassing attributes, therefore bypassing MutationObserver observations.

Seems like `Object.observe` would be the simple magic tool that I keep circling back to.

In my specific use case (the behaviors), I'd just like to observe arbitrary instance props so that I can get performance gains from not observing attribute changes in cases where a framework is using instance props instead of attributes. Again, attributes are arbitrarily observable, while props are not.

If I am okay to drop support for IE (I'm a little skeptical), then I'd like to consider how Proxy might help, but it just seems more complicated that it ought to be compared to `Object.observe` (which my behaviors could use to observe elements).

/#!/JoePea


On Tue, Jul 24, 2018 at 10:17 AM T.J. Crowder <[hidden email]> wrote:
On Tue, Jul 24, 2018 at 6:01 PM, /#!/JoePea
<[hidden email]> wrote:
> Is there a way to polyfill `Object.observe` in such a way that the object
> before observation is the same reference as the object being observed after
> the call (i.e. not a Proxy), and other than monkey-patching getters/setters?
>
> Is defining getters/setters the only way?

Even that doesn't really polyfill it, because `Object.observe` got notifications of changes when new properties were created as well.

But yes, **if** you know the names of the properties in advance (the "shape" of the object, I believe, is the current parlance?), and if you want notifications of changes just to those properties, I think monkeypatching will be your simplest and most successful approach.

If you need to catch additions as well, I don't think there's any solution other than diffing, which is quite yucky.

-- T.J. Crowder

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Polyfilling Object.observe

Andrea Giammarchi-2
Proxy is limited, because it won't intercept deleteProperty or others as part of a prototype, so you need to replace the object with a proxied object, which is not exactly the same as Object.observe.

On Tue, Jul 24, 2018 at 7:50 PM /#!/JoePea <[hidden email]> wrote:
But yes, **if** you know the names of the properties in advance

I don't because I'm using [`element-behaviors`](https://github.com/trusktr/element-behaviors) (which I wrote). Behaviors can be arbitrarily added and removed from an element, behaviors can observe any arbitrary attributes on an element, but the difficulty is in behaviors observing arbitrary properties on an element regardless of if the props already exist. Because an element may have any number of unknown behaviors added to it in the future, there's no way to pre-meditate the set of props that will be observed.

The only way I can think of that might work will only work if all actions against the observed object happen through methods of the observed object's prototype

If I drop support for IE, maybe Proxy will help me.

The thing is, what are the implications? It seems a bit tricky to introduce a Proxy somewhere in the middle of a class hierarchy.

- How do I return a Proxied this from a class in the middle of a hierarchy without changing patterns? Seems like I could use a single base `constructor` then move all construction logic to a `construct` method called by the base `constructor`, to make things easier. I do a similar hack now anyways in order to make ES5-style classes work with native Custom Elements.
- How do we proxify a class prototype when using ES6 classes? Do we just `SomeClass.prototype = new Proxy(SomeClass.prototype, handler)`? Any implications of that?
- Seems like `Object.observe` would've just been the easiest way to achieve what I want, if that hadn't been dropped.
- I currently use `MutationObserver` to observe HTML element attribute changes, which is the like the equivalent of `Object.observe` for DOM. But the downside of this is that it only covers attributes, not instance properties. Plus, attributes are always strings, incurring performance overhead. And all the new frameworks delegate to instance properties, bypassing attributes, therefore bypassing MutationObserver observations.

Seems like `Object.observe` would be the simple magic tool that I keep circling back to.

In my specific use case (the behaviors), I'd just like to observe arbitrary instance props so that I can get performance gains from not observing attribute changes in cases where a framework is using instance props instead of attributes. Again, attributes are arbitrarily observable, while props are not.

If I am okay to drop support for IE (I'm a little skeptical), then I'd like to consider how Proxy might help, but it just seems more complicated that it ought to be compared to `Object.observe` (which my behaviors could use to observe elements).

/#!/JoePea


On Tue, Jul 24, 2018 at 10:17 AM T.J. Crowder <[hidden email]> wrote:
On Tue, Jul 24, 2018 at 6:01 PM, /#!/JoePea
<[hidden email]> wrote:
> Is there a way to polyfill `Object.observe` in such a way that the object
> before observation is the same reference as the object being observed after
> the call (i.e. not a Proxy), and other than monkey-patching getters/setters?
>
> Is defining getters/setters the only way?

Even that doesn't really polyfill it, because `Object.observe` got notifications of changes when new properties were created as well.

But yes, **if** you know the names of the properties in advance (the "shape" of the object, I believe, is the current parlance?), and if you want notifications of changes just to those properties, I think monkeypatching will be your simplest and most successful approach.

If you need to catch additions as well, I don't think there's any solution other than diffing, which is quite yucky.

-- T.J. Crowder
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Polyfilling Object.observe

T.J. Crowder-2
In reply to this post by /#!/JoePea
On Tue, Jul 24, 2018 at 6:50 PM, /#!/JoePea
<[hidden email]> wrote:
>> I don't think there's any solution other than diffing
>
> And how would you diff without polling (while supporting IE)?

When you diff is totally up to your use case. IIRC, AngularJS used to do it upon exit from event handlers (or possibly both entry and exit) or when the application code asked it to.

I think this thread is getting fairly off-topic for this list, though. Perhaps do up a full runnable MCVE and post a Stack Overflow question or similar.

Good luck with it!

-- T.J. Crowder

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Polyfilling Object.observe

/#!/JoePea
In reply to this post by /#!/JoePea
> I don't think there's any solution other than diffing

And how would you diff without polling (while supporting IE)?

`Proxy` is powerful, but it's not as good as `Object.observe` would've been for some very simple tasks.

Every time I wish I could use `Proxy` in a simple way, there's always some issue with it. For example: https://jsfiddle.net/trusktr/hwfontLc/17

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`.

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`.

Using `Proxy` like this is simply not ideal compared to `Object.observe`.

> not exactly the same as Object.observe

Yep :)

> When you diff is totally up to your use case

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.

I simply would like to observe an object with a simple API like:

```js
import someObject from 'any-npm-package-that-could-ever-exist'

const thePropsIWantToObserve = ['foo', 'bar', 'baz']

Object.observeProps( someObject, thePropsIWantToObserve, (name, oldValue, newValue) => {
  console.log('property on someObject changed:', name, oldValue, newValue)
})
```

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.

---

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. If one builds a tank (an API) and places a user in it, that user can't shoot themselves in the foot, can they? (I'm anti-war pro-peace and against violence, that's just an analogy.) 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. 

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.

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? 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.

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`:
ideally we'd only need to write `someObject.foo = 123` which saves both the author of `someObject` and the consumer of `someObject` time.

It'd simply be so nice to have `Object.observe` (and preferably a simpler version, like my following example).

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,

1. It doesn't consider inherited getters/setters.
2. It doesn't consider that `isObserved` can be deleted if someone else sets a new descriptor on top of the observed descriptor.
3. It may trigger unwanted extra side-effects by call getters more than once.
4. etc.

`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)!

So the following is what I'm using, which works in my specific use cases where the above caveats are not a problem:

```js
const isObserved = Symbol()

function observe(object, propertyNames, callback) {
    let map

    for (const propName of propertyNames) {
        const descriptor = Object.getOwnPropertyDescriptor(object, propName) || {}

        if (descriptor[isObserved]) continue

        let getValue
        let setValue

        if (descriptor.get || descriptor.set) {
            // we will use the existing getter/setter assuming they don't do
            // anyting crazy that we might not expect. (See? Another reason for
            // Object.observe)
            const oldGet = descriptor.get
            const oldSet = descriptor.set

            getValue = () => oldGet.call(object)
            setValue = value => oldSet.call(object, value)
        }
        else {
            if (!map) map = new Map

            const initialValue = descriptor.value
            map.set(propName, initialValue)

            delete descriptor.value
            delete descriptor.writable

            getValue = () => map.get(propName)
            setValue = value => map.set(propName, value)
        }

        Object.defineProperty(object, propName, {
            ...descriptor,

            get() {
                return getValue()
            },

            set(value) {
                setValue(value)
                callback(propName, getValue())
            },

            [isObserved]: true,
        })
    }
}
```

And the usage looks like:

```js
const o = {
    foo: 1,
    bar: 2,
    get baz() {
        console.log('original get baz')
        return this._baz
    },
    set baz(v) {
        console.log('original set baz')
        this._baz = v
    },
}

observe(o, ['foo', 'bar', 'baz'], (propName, newValue) => {
    console.log('changed value:', propName, newValue)
})

o.foo = 'foo'
o.bar = 'bar'
o.baz = 'baz'
```

/#!/JoePea


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Polyfilling Object.observe

/#!/JoePea
Another caveat of my implementation, for example, is that it adds getters/setters for properties that previously didn't exist. This will break code that checks existence of props. etc. etc. That's not a problem with a theoretical `Object.observe`.

/#!/JoePea


On Fri, Jul 27, 2018 at 10:53 AM /#!/JoePea <[hidden email]> wrote:
> I don't think there's any solution other than diffing

And how would you diff without polling (while supporting IE)?

`Proxy` is powerful, but it's not as good as `Object.observe` would've been for some very simple tasks.

Every time I wish I could use `Proxy` in a simple way, there's always some issue with it. For example: https://jsfiddle.net/trusktr/hwfontLc/17

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`.

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`.

Using `Proxy` like this is simply not ideal compared to `Object.observe`.

> not exactly the same as Object.observe

Yep :)

> When you diff is totally up to your use case

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.

I simply would like to observe an object with a simple API like:

```js
import someObject from 'any-npm-package-that-could-ever-exist'

const thePropsIWantToObserve = ['foo', 'bar', 'baz']

Object.observeProps( someObject, thePropsIWantToObserve, (name, oldValue, newValue) => {
  console.log('property on someObject changed:', name, oldValue, newValue)
})
```

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.

---

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. If one builds a tank (an API) and places a user in it, that user can't shoot themselves in the foot, can they? (I'm anti-war pro-peace and against violence, that's just an analogy.) 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. 

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.

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? 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.

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`:
ideally we'd only need to write `someObject.foo = 123` which saves both the author of `someObject` and the consumer of `someObject` time.

It'd simply be so nice to have `Object.observe` (and preferably a simpler version, like my following example).

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,

1. It doesn't consider inherited getters/setters.
2. It doesn't consider that `isObserved` can be deleted if someone else sets a new descriptor on top of the observed descriptor.
3. It may trigger unwanted extra side-effects by call getters more than once.
4. etc.

`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)!

So the following is what I'm using, which works in my specific use cases where the above caveats are not a problem:

```js
const isObserved = Symbol()

function observe(object, propertyNames, callback) {
    let map

    for (const propName of propertyNames) {
        const descriptor = Object.getOwnPropertyDescriptor(object, propName) || {}

        if (descriptor[isObserved]) continue

        let getValue
        let setValue

        if (descriptor.get || descriptor.set) {
            // we will use the existing getter/setter assuming they don't do
            // anyting crazy that we might not expect. (See? Another reason for
            // Object.observe)
            const oldGet = descriptor.get
            const oldSet = descriptor.set

            getValue = () => oldGet.call(object)
            setValue = value => oldSet.call(object, value)
        }
        else {
            if (!map) map = new Map

            const initialValue = descriptor.value
            map.set(propName, initialValue)

            delete descriptor.value
            delete descriptor.writable

            getValue = () => map.get(propName)
            setValue = value => map.set(propName, value)
        }

        Object.defineProperty(object, propName, {
            ...descriptor,

            get() {
                return getValue()
            },

            set(value) {
                setValue(value)
                callback(propName, getValue())
            },

            [isObserved]: true,
        })
    }
}
```

And the usage looks like:

```js
const o = {
    foo: 1,
    bar: 2,
    get baz() {
        console.log('original get baz')
        return this._baz
    },
    set baz(v) {
        console.log('original set baz')
        this._baz = v
    },
}

observe(o, ['foo', 'bar', 'baz'], (propName, newValue) => {
    console.log('changed value:', propName, newValue)
})

o.foo = 'foo'
o.bar = 'bar'
o.baz = 'baz'
```

/#!/JoePea


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Polyfilling Object.observe

Oriol _
In reply to this post by /#!/JoePea

> Every time I wish I could use `Proxy` in a simple way, there's always some issue with it. For example: https://jsfiddle.net/trusktr/hwfontLc/17

The main problem with that code is that the `prototype` property of a class is read-only, that's why you see no output. Blame classes instead of proxies.
And then inside the proxy traps you should use the target instead of the receiver if you want to avoid infinite recursion.
I have never had issues with proxies.

>
So the following is what I'm using
Your function has various problems like assuming that you can define symbol properties in property descriptors and read them later, ignoring the receiver when calling getters and setters, assuming all objects are extensible and all properties are configurable, etc.

--Oriol

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

pEpkey.asc (3K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Polyfilling Object.observe

Alex Vincent
In reply to this post by /#!/JoePea

`Proxy` is powerful, but it's not as good as `Object.observe` would've been for some very simple tasks.

Every time I wish I could use `Proxy` in a simple way, there's always some issue with it. For example: https://jsfiddle.net/trusktr/hwfontLc/17

Because you are doing it wrong.

Proxies can *only* observe on the object that they're created for.  By setting Foo.prototype to a proxy, you can only observe operations on Foo.prototype, not instances of Foo.

What you really want is to have new Foo() return a Proxy... which is counter-intuitive, admittedly, because we're all taught that a constructor should never explicitly return a value. 

This problem has been solved, a few times, with the introduction of membranes in JavaScript.  In that environment, you would start with Foo and wrap it in a membrane proxy. Then, in invoking new Foo(), the "construct" trap of that proxy would return another roxy automatically, probably using the same proxy handler but a different proxy target.

If it's any consolation, proxies are in general very hard to work with.  You're only scratching the surface here.  I recently gave a talk at TC39 (the standards body for ECMAScript) on membranes.  One key takeaway is that the overhead in dealing with membrane-oriented proxies really is better off left to a library built for that purpose.

Tom van Cutsem is working on an article summarizing the current state of membranes.  I'm not sure if he has approved its general release yet, so stay tuned...

Alex Vincent
Edmonds, WA (on vacation)

--
"The first step in confirming there is a bug in someone else's work is confirming there are no bugs in your own."
-- Alexander J. Vincent, June 30, 2001

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Polyfilling Object.observe

/#!/JoePea
Because you are doing it wrong.

Yeah, because using Proxy isn't simple like using `Object.observe`. I'm just showing that this is all extremely easy to do with `Object.observe`.

> What you really want is to have new Foo() return a Proxy... which is counter-intuitive, admittedly, because we're all taught that a constructor should never explicitly return a value.  

I mentioned earlier I don't want to modify my entire class hierarchy just to make part of it observable, and have to tell people who extend my classes to change their patterns (it's a breaking change).

Again, not a problem with `Object.observe`.

> membranes

I just read the articles. That's much too complicated. Plus, it only works on objects that are designed to be proxies before they are passed to other code.

I want to do this:

```js
import anyObject from 'any-package-on-npm'

Object.observe(anyObject, console.log)

// logs stuff every time any-package-on-npm modifies the object.
```

If I use proxy, this is what happens:


```js
import anyObject from 'any-package-on-npm'

const membrane = new ObservableMembrane({
  valueMutated: console.log
})

membrane.getProxy(anyObject)

// nothing, silence, crickets
```

`Object.observe` just works, and is soooooooooooooooo simple. I won't shoot myself in the foot with it. I'd like to make a one-way data flow implementation with it, using 3rd party web components, and `Object.observe` would make this _incredibly simple_.

/#!/JoePea


On Sat, Jul 28, 2018 at 8:54 AM Alex Vincent <[hidden email]> wrote:

`Proxy` is powerful, but it's not as good as `Object.observe` would've been for some very simple tasks.

Every time I wish I could use `Proxy` in a simple way, there's always some issue with it. For example: https://jsfiddle.net/trusktr/hwfontLc/17

Because you are doing it wrong.

Proxies can *only* observe on the object that they're created for.  By setting Foo.prototype to a proxy, you can only observe operations on Foo.prototype, not instances of Foo.

What you really want is to have new Foo() return a Proxy... which is counter-intuitive, admittedly, because we're all taught that a constructor should never explicitly return a value. 

This problem has been solved, a few times, with the introduction of membranes in JavaScript.  In that environment, you would start with Foo and wrap it in a membrane proxy. Then, in invoking new Foo(), the "construct" trap of that proxy would return another roxy automatically, probably using the same proxy handler but a different proxy target.

If it's any consolation, proxies are in general very hard to work with.  You're only scratching the surface here.  I recently gave a talk at TC39 (the standards body for ECMAScript) on membranes.  One key takeaway is that the overhead in dealing with membrane-oriented proxies really is better off left to a library built for that purpose.

Tom van Cutsem is working on an article summarizing the current state of membranes.  I'm not sure if he has approved its general release yet, so stay tuned...

Alex Vincent
Edmonds, WA (on vacation)

--
"The first step in confirming there is a bug in someone else's work is confirming there are no bugs in your own."
-- Alexander J. Vincent, June 30, 2001
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss