Re: Polyfilling Object.observe

2018-07-28 Thread #!/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 s 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  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
> es-discuss@mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Polyfilling Object.observe

2018-07-28 Thread Alex Vincent
> `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
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Polyfilling Object.observe

2018-07-28 Thread Oriol _
> 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


pEpkey.asc
Description: pEpkey.asc
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Polyfilling Object.observe

2018-07-27 Thread /#!/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 p

Re: Polyfilling Object.observe

2018-07-27 Thread /#!/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  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 trig

Re: Polyfilling Object.observe

2018-07-24 Thread T.J. Crowder
On Tue, Jul 24, 2018 at 6:50 PM, /#!/JoePea
 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
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Polyfilling Object.observe

2018-07-24 Thread Andrea Giammarchi
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  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 <
> tj.crow...@farsightsoftware.com> wrote:
>
>> On Tue, Jul 24, 2018 at 6:01 PM, /#!/JoePea
>>  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
> es-discuss@mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Polyfilling Object.observe

2018-07-24 Thread /#!/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 <
tj.crow...@farsightsoftware.com> wrote:

> On Tue, Jul 24, 2018 at 6:01 PM, /#!/JoePea
>  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
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Polyfilling Object.observe

2018-07-24 Thread T.J. Crowder
On Tue, Jul 24, 2018 at 6:01 PM, /#!/JoePea
 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
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Polyfilling Object.observe

2018-07-24 Thread 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  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
> es-discuss@mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss