Experimental implementation of Object.observe & JS Utility library now available

# Rafael Weinstein (13 years ago)

As promised, here is an experimental implementation of the Object.observe strawman in a branch of v8:

rafaelw/v8

[Note that there are Mac & Windows binaries of Chromium for everyone's convenience. The links to the binaries are contained in the README.md -- and also at the bottom of the page].

Also, here is a JS Utility library which uses the Object.observe() mechanism and exposes some higher-level and more convenient abstractions (such as path observation & array splice computations):

rafaelw/ChangeSummary.

[Note that this library is to Object.observe as MutationSummary (code.google.com/p/mutation-summary) is to DOM Mutation Observers]


Expect (and report) bugs in both and crashes in the v8 impl, but it should be stable enough now for actual experimentation & prototyping.

Unless someone feels otherwise, we just assume any feedback go directly es-discuss (to this thread is fine) so as not to split away from here any substantive discussion about the feature.


Our next step is to align our tests and the ChangeSummary library with Tom's excellent proxy implementation so we'll have two experimental versions which pass the same set of tests (and we can continue to use Tom's proxy impl as a reference spec-in-code].

Enjoy

# Brandon Benvie (13 years ago)

I love it.

# Andrea Giammarchi (13 years ago)

just seen it, looks like an improved alternative to the good old, non standard, Object.prototype.watch ... but

  1. records are not "struct like", properties might be or might not be there rather than being there with undefined values ... is this heading to not optimal performances in V8 and others?
  2. how is the memory consumption preserved with freshly new created record objects, rather than a single callback as entry point and simply oldValue, newValue passed as arguments?
  3. why the callback is not simply something like observer(type, object, property, oldValue, newValue) so that a single callback can be still recyclable and no object is created per each change, plus it's easier to retrieve the new value rather than passing through record.object[ record.name] ?
  4. what is the context of each notify call? (this might be in specs already, so I'll check there too)

I know it's strawman already but if anyone can explain me these points that would be great, cheers.

br

# Alex Russell (13 years ago)

On Tue, Aug 14, 2012 at 10:33 AM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:

just seen it, looks like an improved alternative to the good old, non standard, Object.prototype.watch

Key differences include:

  • unlike watch(), you can be informed not only of deletions but also additions to objects
  • delivery is async-ish (huzzah!)

... but

  1. records are not "struct like", properties might be or might not be there rather than being there with undefined values ... is this heading to not optimal performances in V8 and others?

I don't understand. New objects with fixed-ish shape have fixed-ish shape

through their lifetimes. It's unclear what the problem is here.

  1. how is the memory consumption preserved with freshly new created record objects, rather than a single callback as entry point and simply oldValue, newValue passed as arguments?

These objects are disposed at the end of the current turn (assuming you

don't hold on to them forever).

  1. why the callback is not simply something like observer(type, object, property, oldValue, newValue) so that a single callback can be still recyclable and no object is created per each change, plus it's easier to retrieve the new value rather than passing through record.object[ record.name] ?

Having the full list is critical for dealing with inter-related changes

that happen in a second turn. You often care about the high-level semantic action, not just the immediate change to some property.

# Andrea Giammarchi (13 years ago)

if fixed-ish shape is not a performance issue then is fine, still I would put in this fixed-ish shape a currentValue property, not only the oldValue one, to avoid constantly and repeated checks to record.object[record.name]

If not holded, GC has to take care of them in any case, isn't it? I know objects as arguments are better/easier to extend/maintain than fixed callback contracts/signatures but sometimes the creation of an object per each callback call, and with this mechanism I can see many of them, could have a not desired cost in therms of memory and performances.

Thanks in any case for the reply.

br

# Erik Arvidsson (13 years ago)

On Tue, Aug 14, 2012 at 5:31 PM, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:

if fixed-ish shape is not a performance issue then is fine, still I would put in this fixed-ish shape a currentValue property, not only the oldValue one, to avoid constantly and repeated checks to record.object[record.name]

The idea behind not having currentValue was that it exposes information that cannot be gotten using polling. I'm not sure if that is really true because the current value will be exposed as the old value when the next change record is delivered.

# Andrea Giammarchi (13 years ago)

so write on specs that record.object[record.name] won't point to the actual value ... and let's see the outcome ( I mean ... sorry, what? )

if oldValue is granted, currentValue should be granted as well I guess, at least per each notification stack. How come this is not the case?

# Rafael Weinstein (13 years ago)

I've created a bunch of cool demos and a video which attempts to explain the use cases & need for Object.observe().

plus.google.com/u/1/111386188573471152118

# Andrea Giammarchi (13 years ago)

I wish this is landing in Canary ASAP, I understand everything is doable through Proxy but:

  1. Proxy is nowhere, Proxy is more complicated
  2. Proxy means dealing with objects wrappers and wrappers owners, Object.observe deals with the object everyone is using, not its own wrap
  3. using some sort of "ticks based events queue", combined with DOM mutation events, Object.observe is way easier to shim while Proxy are impossible to shim

This Object.observe "unaccepted" strawman thing, is the best proposal I have seen in a while ... I am all in for := , class, super and stuff, but that's not really what nowadays web applications need, Object.observe is, and this is, of course, just my opinion.

br, Andrea Giammarchi

# gaz Heyes (13 years ago)

Would this proposal work on the Object prototype? If so then it could be used for JSON hijacking. I'd recommend it didn't.

# Andrea Giammarchi (13 years ago)

the Notifier is lazily instantiated and I believe not enumerable so JSON.stringify should ever expose this property.

Moreover, it does not look like there is a {}.notifier property anywhere, Object.getNotifier(obj) is required indeed so a WeakMap that relates the obj, and its notifier, cannot be serialized in a meaningful way via JSON.stringify neither.

As summary, and correct me if I am wrong, Object.observe is safe, also because observers are callbacks and these, again, cannot be represented in a meaningful way in JSON.

br

# Andrea Giammarchi (13 years ago)

typo: should never expose the not enumerable property

# gaz Heyes (13 years ago)

On 17 August 2012 10:57, Andrea Giammarchi <andrea.giammarchi at gmail.com>wrote:

the Notifier is lazily instantiated and I believe not enumerable so JSON.stringify should ever expose this property.

Moreover, it does not look like there is a {}.notifier property anywhere, Object.getNotifier(obj) is required indeed so a WeakMap that relates the obj, and its notifier, cannot be serialized in a meaningful way via JSON.stringify neither.

Ah no I wasn't talking about the JSON object but remotely hacking JSON feeds themselves using external scripts and hijacking the Object prototype with Observe.

# Andrea Giammarchi (13 years ago)

As far as I can tell, Object.observe(obj) create a notifier in the obj itself which means, after that, if you Object.prototype.whatever = 123; the obj notifier won't fire since it's not its own property and I do hope this works with own properties only otherwise not only we can have hijacking attacks but tremendous performance impact ( if a notifier has to automatically create a notifier up to the proto chain until the null prototype ... a non-sense, imho )

# Erik Arvidsson (13 years ago)

Yes, only own properties are taken into account.

I think there might be some open issues related to proto depending on whether it ends up being an accessor or magic data property in the end.

# Rafael Weinstein (13 years ago)

Thanks so much for your time.

Much care has been taking with this proposal to ensure that it is neutral with respect to the existing JS Object/Security model.

As I understand it, the core vulnerability with JSON hacking is the ability to define getters on the Object prototype. Object.observe() does not affect that ability.

In order to be notified of changes to an object, you need a reference to it first. E.g.

Object.observe(Object.prototype, function doBadThings() { .. });

But if you were able to do this, you could have just as easily gone ahead and done bad things directly to the Object.prototype. Object.observe() doesn't increase your access.

If there is something I'm missing, perhaps you can provide a code example of how the attack would work.

# gaz Heyes (13 years ago)

On 17 August 2012 13:47, Rafael Weinstein <rafaelw at chromium.org> wrote:

Hi gaz,

Thanks so much for your time.

Much care has been taking with this proposal to ensure that it is neutral with respect to the existing JS Object/Security model.

As I understand it, the core vulnerability with JSON hacking is the ability to define getters on the Object prototype. Object.observe() does not affect that ability.

The original attack I'm talking about is this: //variant of the "I know what your friends did last summer" attack // www.thespanner.co.uk/2009/01/07/i-know-what-your-friends-did-last-summer <script>

Object.defineProperty(Object.prototype, "x", { set:function(val){ alert(val); } }); </script> <script src="//some.external.site/friends.json"></script> <!-- friends.json contains [{"x":"stolen"}] -->

This was patched to prevent the setter being called on a new object literal but I guess if the observable stuff doesn't account for this then it's a problem. It seems like you account for this which is cool.

# Andrea Giammarchi (13 years ago)

that object has to be created first, than parsed to Object.observe ... even evaluating this:

[Object.observe({"x":"stolen"}, dangerousStuff)]

nothing will happen since the object has been defined already

;)

# gaz Heyes (13 years ago)

On 17 August 2012 14:31, Andrea Giammarchi <andrea.giammarchi at gmail.com>wrote:

that object has to be created first, than parsed to Object.observe ... even evaluating this:

[Object.observe({"x":"stolen"}, dangerousStuff)]

nothing will happen since the object has been defined already

So basically you can't observe the Object prototype it has to be a object literal. I think that answers my question :)

# Andrea Giammarchi (13 years ago)

You can observe the Object.prototype same you can define setters and stuff there ... observing it won't affect literals or new object creation since it works with own properties.

# Brandon Benvie (13 years ago)

I agree on the above with regard to Proxies. They are awesome and allow for incredible things, but Object.observe fills a different use case that I think is more common at at the user standpoint or at least library standpoint. When you look around at the major JS libraries that exist the problem they are trying to solve (after DOM normalization) is data-binding. Proxy can be used to solve this for new objects or wrapped objects, but that's overkill and may have performance consequences, and has no support for working with existing objects. Proxy and observe end up filling two completely different use-cases, and I would venture to say that observe is the one that most people could make better use of if they had it in their hands today.

# Brendan Eich (13 years ago)

All praise to Raf et al., my concern is that something synchronous, plus event loop concurrency and setImmediate, would suffice to bootstrap the rather more elaborate proposal on top of the simpler O.p.watch like fundament.

This is not to say we shouldn't standardize higher-level APIs, and instead push that off on library authors to write, and users to download. There's a delicate balance here. We often screw up higher-level abstractions in annoying ways, but perhaps that risk is worth taking.

What I'm really getting at is this: why not expose the synchronous primitive and e.l.c. building blocks as well?

# Tab Atkins Jr. (13 years ago)

On Fri, Aug 17, 2012 at 6:49 PM, Brendan Eich <brendan at mozilla.org> wrote:

All praise to Raf et al., my concern is that something synchronous, plus event loop concurrency and setImmediate, would suffice to bootstrap the rather more elaborate proposal on top of the simpler O.p.watch like fundament.

This is not to say we shouldn't standardize higher-level APIs, and instead push that off on library authors to write, and users to download. There's a delicate balance here. We often screw up higher-level abstractions in annoying ways, but perhaps that risk is worth taking.

What I'm really getting at is this: why not expose the synchronous primitive and e.l.c. building blocks as well?

For the same reason we're killing MutationEvents, rather than just promoting MutationObservers as a better alternative - synchronous change notifications slow things down considerably, and they're very prone to making the browser crashy. Get the slightest thing wrong in your implementation, and suddenly the user is holding onto a null pointer.

# Brendan Eich (13 years ago)

Tab Atkins Jr. wrote:

On Fri, Aug 17, 2012 at 6:49 PM, Brendan Eich<brendan at mozilla.org> wrote:

All praise to Raf et al., my concern is that something synchronous, plus event loop concurrency and setImmediate, would suffice to bootstrap the rather more elaborate proposal on top of the simpler O.p.watch like fundament.

This is not to say we shouldn't standardize higher-level APIs, and instead push that off on library authors to write, and users to download. There's a delicate balance here. We often screw up higher-level abstractions in annoying ways, but perhaps that risk is worth taking.

What I'm really getting at is this: why not expose the synchronous primitive and e.l.c. building blocks as well?

For the same reason we're killing MutationEvents, rather than just promoting MutationObservers as a better alternative - synchronous change notifications slow things down considerably,

Rgr that.

The idea I am promoting is opposed by not only janky browser code (all browsers, some are better than others), but janky JS. Still, someone has to write the sync stubs and defer the real programmable work via an async API. Sounds like your argument is "let the experts [browser vendors] do the sync stubs."

and they're very prone to making the browser crashy. Get the slightest thing wrong in your implementation, and suddenly the user is holding onto a null pointer.

This you'll have to explain more. What pointer, to what point-ee? Why nulled?

# Mark S. Miller (13 years ago)

On Fri, Aug 17, 2012 at 6:49 PM, Brendan Eich <brendan at mozilla.org> wrote:

All praise to Raf et al., my concern is that something synchronous, plus event loop concurrency and setImmediate, would suffice to bootstrap the rather more elaborate proposal on top of the simpler O.p.watch like fundament.

This is not to say we shouldn't standardize higher-level APIs, and instead push that off on library authors to write, and users to download. There's a delicate balance here. We often screw up higher-level abstractions in annoying ways, but perhaps that risk is worth taking.

What I'm really getting at is this: why not expose the synchronous primitive and e.l.c. building blocks as well?

A synchronous observation mechanism provides an attacker too many opportunities for a plan interference attack. If you'll recall, an earlier synchronous proposal died for this reason.

Object.observe has been carefully designed to strike a good balance between providing non-malicious code useful new powers without providing attackers significant new attack opportunities. The core principle is that a client of an object can anyway observe changes to observable state asynchronously by polling between turns. And a client of an object, if it receives control during a turn, can poll while it has control. To a first approximation, Object.observe can be understood as an optimization of polling. It is actually more powerful than that, but in ways that are beneficial without IMO creating significant new hazards.

# Brendan Eich (13 years ago)

Mark S. Miller wrote:

On Fri, Aug 17, 2012 at 6:49 PM, Brendan Eich <brendan at mozilla.org <mailto:brendan at mozilla.org>> wrote:

All praise to Raf et al., my concern is that something
synchronous, plus event loop concurrency and setImmediate, would
suffice to bootstrap the rather more elaborate proposal on top of
the simpler O.p.watch like fundament.

This is not to say we shouldn't standardize higher-level APIs, and
instead push that off on library authors to write, and users to
download. There's a delicate balance here. We often screw up
higher-level abstractions in annoying ways, but perhaps that risk
is worth taking.

What I'm really getting at is this: why not expose the synchronous
primitive and e.l.c. building blocks as well?

A synchronous observation mechanism provides an attacker too many opportunities for a plan interference attack. If you'll recall, an earlier synchronous proposal died for this reason.

Thanks, I recall -- but this needs to be stated clearly in the spec. I hear from people all the time asking Y U no Object.prototype.watch.

# Rafael Weinstein (13 years ago)

On Fri, Aug 17, 2012 at 8:03 PM, Brendan Eich <brendan at mozilla.org> wrote:

Mark S. Miller wrote:

On Fri, Aug 17, 2012 at 6:49 PM, Brendan Eich <brendan at mozilla.org <mailto:brendan at mozilla.org>> wrote:

All praise to Raf et al., my concern is that something
synchronous, plus event loop concurrency and setImmediate, would
suffice to bootstrap the rather more elaborate proposal on top of
the simpler O.p.watch like fundament.

This is not to say we shouldn't standardize higher-level APIs, and
instead push that off on library authors to write, and users to
download. There's a delicate balance here. We often screw up
higher-level abstractions in annoying ways, but perhaps that risk
is worth taking.

What I'm really getting at is this: why not expose the synchronous
primitive and e.l.c. building blocks as well?

A synchronous observation mechanism provides an attacker too many opportunities for a plan interference attack. If you'll recall, an earlier synchronous proposal died for this reason.

That is an excellent reason. I have two others:

  1. It's a terrible design pattern to encourage. Webdevs will absolutely use it and learn the hard way, just like we did with Mutation Events that it works fine when we start, but down the road lies nothing but pain and suffering.

  2. Synchronous doesn't actually exist -- it's a false option.

To explain: When people talk about "synchronous" what they expect is that they will be notified concurrent with the event happening (this is happening). The expectation is that when they are invoked they are observing the world as the event is describing.

This is the appeal of synchronous delivery. Here's the rub: unless you plan to only allow a single observer per object, then you have to pick someone to go first. And unless you plan to prevent that person from further mutating the object, the next guy to be called may not observe the world in the state suggested by the "synchronous" call.

In fact, an arbitrary number of mutations can always have occurred by the time any given observer is called, so it's just dishonest API which pretends that a single thing has happened. The correct thing is to inform each observer what set of things has happened.

So the only questions are:

  1. Do you mislead the consumer with API which suggests that only one thing will has happened
  2. If not, when do you deliver the set of things: immediately after the mutation occurs, at the end of the turn or ask the UA to schedule a future task.

Referring back to my reason (1) This question was debated extensively with DOM Mutation Observers and unanimously decided that the end of the turn was the only good solution. "immediately" puts all code in danger of having its runtime assumptions invalidated after every operation which mutates objects and "future task" is far too late to be useful for most almost all use cases.

Thanks, I recall -- but this needs to be stated clearly in the spec. I hear from people all the time asking Y U no Object.prototype.watch.

Which spec? Is there something you're wanting to see in the text of the Object.observe proposal?

# Tab Atkins Jr. (13 years ago)

On Fri, Aug 17, 2012 at 7:30 PM, Brendan Eich <brendan at mozilla.org> wrote:

and they're very prone to making the browser crashy. Get the slightest thing wrong in your implementation, and suddenly the user is holding onto a null pointer.

This you'll have to explain more. What pointer, to what point-ee? Why nulled?

In a perfect implementation, everything's fine.

In real-world implementations, when the user changes the world during their listener invocation, and then the JS engine picks up where it left off and tries to finish the operation that triggered the listener (because mutation events happen during/before the actual mutation), some of those hanging operations are sometimes not properly notified of the world-changes, and bam, null pointer exception (or otherwise invalid operation invoked on a tree that the operation didn't expect).

This happens so often, and so widely, that it seems to be a fact of life - the DOM is just too complicated to properly inform everyone of everything that a user could change, when the user has the opportunity to pre-empt some of the operations.

# Brendan Eich (13 years ago)

Rafael Weinstein wrote:

On Fri, Aug 17, 2012 at 8:03 PM, Brendan Eich<brendan at mozilla.org> wrote:

Mark S. Miller wrote:

On Fri, Aug 17, 2012 at 6:49 PM, Brendan Eich<brendan at mozilla.org <mailto:brendan at mozilla.org>> wrote:

 All praise to Raf et al., my concern is that something
 synchronous, plus event loop concurrency and setImmediate, would
 suffice to bootstrap the rather more elaborate proposal on top of
 the simpler O.p.watch like fundament.

 This is not to say we shouldn't standardize higher-level APIs, and
 instead push that off on library authors to write, and users to
 download. There's a delicate balance here. We often screw up
 higher-level abstractions in annoying ways, but perhaps that risk
 is worth taking.

 What I'm really getting at is this: why not expose the synchronous
 primitive and e.l.c. building blocks as well?

A synchronous observation mechanism provides an attacker too many opportunities for a plan interference attack. If you'll recall, an earlier synchronous proposal died for this reason.

That is an excellent reason.

It is, and I should have recited it to the Y U no kids but I hadn't found it expressed concisely. I should have looked harder -- it was recorded in the May 2011 meeting notes:

esdiscuss/2011-May/014748

""" Allen: This allows people to break into abstractions by putting observers on objects they don't own. DaveH: Proxies are deliberately less powerful than this in that they don't allow you to attach behaviors to arbitrary objects that you don't own. MarkM: Notification happening synchronously is a security problem. Observers can run at times when code doesn't expect malicious changes. """

I have two others:

  1. It's a terrible design pattern to encourage. Webdevs will absolutely use it and learn the hard way, just like we did with Mutation Events that it works fine when we start, but down the road lies nothing but pain and suffering.

This is not a complete reason -- "see mutation events" -- but I know that history too. What I remember mostly dissuading developers was bad performance -- use a mutation event listener and your DOM got slow

# Rafael Weinstein (13 years ago)

On Sat, Aug 18, 2012 at 3:06 PM, Brendan Eich <brendan at mozilla.org> wrote:

Rafael Weinstein wrote:

On Fri, Aug 17, 2012 at 8:03 PM, Brendan Eich<brendan at mozilla.org> wrote:

Mark S. Miller wrote:

On Fri, Aug 17, 2012 at 6:49 PM, Brendan Eich<brendan at mozilla.org <mailto:brendan at mozilla.org>> wrote:

 All praise to Raf et al., my concern is that something
 synchronous, plus event loop concurrency and setImmediate, would
 suffice to bootstrap the rather more elaborate proposal on top of
 the simpler O.p.watch like fundament.

 This is not to say we shouldn't standardize higher-level APIs, and
 instead push that off on library authors to write, and users to
 download. There's a delicate balance here. We often screw up
 higher-level abstractions in annoying ways, but perhaps that risk
 is worth taking.

 What I'm really getting at is this: why not expose the synchronous
 primitive and e.l.c. building blocks as well?

A synchronous observation mechanism provides an attacker too many opportunities for a plan interference attack. If you'll recall, an earlier synchronous proposal died for this reason.

That is an excellent reason.

It is, and I should have recited it to the Y U no kids but I hadn't found it expressed concisely. I should have looked harder -- it was recorded in the May 2011 meeting notes:

esdiscuss/2011-May/014748

""" Allen: This allows people to break into abstractions by putting observers on objects they don't own. DaveH: Proxies are deliberately less powerful than this in that they don't allow you to attach behaviors to arbitrary objects that you don't own. MarkM: Notification happening synchronously is a security problem. Observers can run at times when code doesn't expect malicious changes.

"""

I have two others:

  1. It's a terrible design pattern to encourage. Webdevs will absolutely use it and learn the hard way, just like we did with Mutation Events that it works fine when we start, but down the road lies nothing but pain and suffering.

This is not a complete reason -- "see mutation events" -- but I know that history too. What I remember mostly dissuading developers was bad performance -- use a mutation event listener and your DOM got slow -- but any more detailed history would be helpful, here if not in the spec (more below).

Apologies. My comment wasn't especially helpful or informative. You are absolutely right that the main reason that webdevs avoided them was because they were horrifically slow.

The pain and suffering I was (inelegantly) referring to was that of UA implementors. The pain most acutely felt was that of having to write code which needed to tolerate script tearing the DOM to shreds in the middle of a multiple-step operation.

Basically, if Mutation Events had been fast enough and fully deployed (and thus actually used), webdevs would have ended up feeling that pain that UA implementors were trying to ride themselves of (essentially, writing code that couldn't be counted on to run in isolation)

  1. Synchronous doesn't actually exist -- it's a false option.

To explain: When people talk about "synchronous" what they expect is that they will be notified concurrent with the event happening (this is happening). The expectation is that when they are invoked they are observing the world as the event is describing.

This is the appeal of synchronous delivery. Here's the rub: unless you plan to only allow a single observer per object, then you have to pick someone to go first. And unless you plan to prevent that person from further mutating the object, the next guy to be called may not observe the world in the state suggested by the "synchronous" call.

Synchronous != synchronized.

In fact, an arbitrary number of mutations can always have occurred by the time any given observer is called, so it's just dishonest API which pretends that a single thing has happened. The correct thing is to inform each observer what set of things has happened.

Right, the "is happening" => "has happened" implies the set.

So the only questions are:

  1. Do you mislead the consumer with API which suggests that only one thing will has happened
  2. If not, when do you deliver the set of things: immediately after the mutation occurs, at the end of the turn or ask the UA to schedule a future task.

Referring back to my reason (1) This question was debated extensively with DOM Mutation Observers and unanimously decided that the end of the turn was the only good solution. "immediately" puts all code in danger of having its runtime assumptions invalidated after every operation which mutates objects and "future task" is far too late to be useful for most almost all use cases.

Thanks, this is good and perhaps we don't need to dig up and summarize the mutation observer debate. But I think the spec (proposal and ECMA-262) should record an informative note or summary or something.

Thanks, I recall -- but this needs to be stated clearly in the spec. I hear from people all the time asking Y U no Object.prototype.watch.

Which spec? Is there something you're wanting to see in the text of the Object.observe proposal?

As a start, and edited for concision, but yes: something that makes the case as crisply as possible. Then I'd expect Allen can incorporate it appropriately into ES6 drafts and we can point people at it (I can, at least!).

Thanks for taking the time to write this up.

No problem. I'll get with Arv and Luke and add some terse commentary to the strawman which covers this issue.

Thanks for caring about the problem. =-)

# Andrea Giammarchi (13 years ago)

The Object owner is a bit inconsistent concept in JS + DOM world.

document.body.innerHTML = '<div><a hraf="#">link</a></div>';

is a very basic example ...

  1. was the user owning the document.body ? I don't think so, body is created in any case even if not specified in the markup
  2. was the user able to set mutation event on that element? well, yes, even without ownership
  3. is the user the owner of that link? I don't think so, the DOM is the owner, not the user, who simply relies the fact Mr DOM will create an object for him
  4. is anyone else able to observe mutation of any element? Yes, 'cause there's no way for a user to mark a DOM node as his/her own

Proxy aren't usable as DOM nodes, are they? I cannot el.appendChild(myOwnProxyOfAnElement) also because others would be able to attach mutation events or observe changes in that node or, even worst, invalidate/remove/destroy my own proxy once document.body.innerHTML = ""; is invoked ... what would be the Proxy, usable as DOM element (in a parallel universe), exact behavior?

Object.observe might result into an "easy to abuse" pattern but in JS world we have too many objects that we don't own by default but we would like to observe.

I understand that knowing explicitly which property we want to observe, something possible with Object#watch, could make things more reasonable ( we need to know in advance the property so we kinda feel like owning it ) but Object#watch has its own problems too with ownership, multiple watches, etc.

Object.observe seems to be the best compromise from both worlds, imho.

br

# David Bruant (13 years ago)

Le 19/08/2012 09:40, Andrea Giammarchi a écrit :

The Object owner is a bit inconsistent concept in JS + DOM world.

No and yes, but no when one is careful.

The piece of code that creates an object can be considered as its owner. If some code accesses an object only if it's given a reference to it, then, anything this code can't touch isn't owned. The other way, if you're granted a reference to an object, it's original owner has shared it with you and now, it can be considered that you co-own it. Languages like C++ really can't have a notion of object ownership since casting an integer to a pointer enables to point to anywhere, even to places you don't have explicitely been shared ownership over. ECMAScript 5 strict mode can have a consistent notion of "object ownership" (ES5 non-strict with its scope-invasive indirect eval cannot really). That the "no" part of my answer. ES.next brings in proxies which allow one to share ownership to an object, but under control over what can be done to the object as well as enable to share only for a limited amount of time.

However, current browsers and the DOM have this property that any script running in an HTML page have access to the document object by using the "document" variable. Since no script has created this object, none really owns it. It's just shared by every script in the page. Some call that "ambient authority". That the "yes" part of my answer.

The last piece of my answer can be achieved with CSP. With CSP, you can define which script is allowed to run on your page. The initial (trusted) scripts run and can decide which other script runs under which condition. For example, you can decide to run untrusted scripts within a controlled lexical environment.

Let's say your script is inlined in an HTML page in an script element with id "bla"

document.body.innerHTML = '<div><a hraf="#">link</a></div>';

It doesn't run thanks to CSP, but if you want to run it safely, you can go for:

var $ = document.querySelector;
var o = {
    document: {
        body: $('#someDiv')
    }
}
var commentScript = $('#bla').textContent;
with(o){
    eval(commentScript)
}

When the untrusted script runs, it doesn't have access to the actual document, but just a div. This is an over-simplified example, for the longer and more rigourous version, checkout the Caja project. Module loader will make all of this even easier and less hacky.

In that case with the help of CSP, the untrusted script doesn't have access to the document. With the help of CSP, the only allowed scripts can be considered as initial owners of all "ambient authority". It then becomes their choice to share what they own with other scripts.

is a very basic example ...

  1. was the user owning the document.body ? I don't think so, body is created in any case even if not specified in the markup

Everyone owns it. Unless with CSP and lexical confinment.

  1. was the user able to set mutation event on that element? well, yes, even without ownership

Yes, because everyone owns it in the naive case. No if CSP and lexical confinment is used.

  1. is the user the owner of that link? I don't think so, the DOM is the owner, not the user, who simply relies the fact Mr DOM will create an object for him

Same answer as above. Everyone owns the link in the naive case, only trusted folks in the careful case.

  1. is anyone else able to observe mutation of any element? Yes, 'cause there's no way for a user to mark a DOM node as his/her own

Same answer as above.

# Andrea Giammarchi (13 years ago)

but then, as you write, if I decide to expose an object or I can reach an object and then use Object.observe there is no ownership problem since whoever let me reach that object didn't want to block me doing so ... also because the Object.freeze(exposedObject) is an easy solution where internally, that frozen object, could be attached through a Map or WeakMap to another one not exposed so ... again, this ownership "thingy" for objects anyone could reach does not look like a good reason at all to avoid implementation of Object.observe

The global context and all its namespaces are another example, as well as the whole DOM is since me, the one that would like to create an element and use similar Object.freeze(myOwnElement) operation, cannot prevent other people to add listeners or change styles ... right ?

So as it is, and I am the one saying "that's correct", for listeners, where if you don't own the listener you cannot remove it, I can't see why Object.observe would have so many problems with the ownership ... anyone can observe unless the object is marked as frozen but none can know who else islistening ... and this is actually, in my opinion, good!

Maybe, a good compromise would be to be able to prevent "propagation" of other listeners so that the first one that set a listener, let's call it the owner, can prevent other listeners from being called or flag that listener as "the only one allowed for that object" ?

This could make Object.observe more reasonable without needing to freeze anything still being free to expose objects ... just saying, to me that would work in any case ( a secured observation ).

Thoughts, welcome

# John J Barton (13 years ago)

On Fri, Aug 17, 2012 at 9:49 PM, Rafael Weinstein <rafaelw at chromium.org>wrote:

A synchronous observation mechanism provides an attacker too many opportunities for a plan interference attack. If you'll recall, an earlier

synchronous proposal died for this reason.

That is an excellent reason. I have two others:

  1. It's a terrible design pattern to encourage. Webdevs will absolutely use it and learn the hard way, just like we did with Mutation Events that it works fine when we start, but down the road lies nothing but pain and suffering.

  2. Synchronous doesn't actually exist -- it's a false option.

To explain: When people talk about "synchronous" what they expect is that they will be notified concurrent with the event happening (this is happening). The expectation is that when they are invoked they are observing the world as the event is describing.

This is the appeal of synchronous delivery. Here's the rub: unless you plan to only allow a single observer per object, then you have to pick someone to go first. And unless you plan to prevent that person from further mutating the object, the next guy to be called may not observe the world in the state suggested by the "synchronous" call.

In fact, an arbitrary number of mutations can always have occurred by the time any given observer is called, so it's just dishonest API which pretends that a single thing has happened. The correct thing is to inform each observer what set of things has happened.

So the only questions are:

  1. Do you mislead the consumer with API which suggests that only one thing will has happened
  2. If not, when do you deliver the set of things: immediately after the mutation occurs, at the end of the turn or ask the UA to schedule a future task.

Referring back to my reason (1) This question was debated extensively with DOM Mutation Observers and unanimously decided that the end of the turn was the only good solution. "immediately" puts all code in danger of having its runtime assumptions invalidated after every operation which mutates objects and "future task" is far too late to be useful for most almost all use cases.

I can understand how batched changes and end-of-turn asynchronous calls are a good match. The development model is now "here are the changes from this turn" rather than "here is a change".

But I don't understand how end-of-turn asynchronous calls helps with the multiple independent listeners problem. If a listener can mutate the DOM, then change-listeners depend upon change-listeners. The relative timing of the listener calls and the non-listener DOM mutations does not change that dependency.

And once change-listeners mutate the DOM then the API of "here are the changes from this turn" again misleads the developer relying on it.

(I think simplifying the internal browser logic for DOM mutation is all the justification needed for new API for DOM mutation observers).

jjb

# Alex Russell (13 years ago)

The core improvement for Object.observe() here is that instead of delivering nested changes, unrolling of observers happens one after the other. The design of the system never puts observers on the stack on top of each other, meaning that whatever happens as a result of code you happen to call will be the "naked" result of that synchronous call, not the AOP-style intercepted result.

Think after advice, not around advice.

# Rafael Weinstein (13 years ago)

On Sun, Aug 19, 2012 at 11:25 AM, John J Barton <johnjbarton at johnjbarton.com> wrote:

On Fri, Aug 17, 2012 at 9:49 PM, Rafael Weinstein <rafaelw at chromium.org> wrote:

A synchronous observation mechanism provides an attacker too many opportunities for a plan interference attack. If you'll recall, an earlier synchronous proposal died for this reason.

That is an excellent reason. I have two others:

  1. It's a terrible design pattern to encourage. Webdevs will absolutely use it and learn the hard way, just like we did with Mutation Events that it works fine when we start, but down the road lies nothing but pain and suffering.

  2. Synchronous doesn't actually exist -- it's a false option.

To explain: When people talk about "synchronous" what they expect is that they will be notified concurrent with the event happening (this is happening). The expectation is that when they are invoked they are observing the world as the event is describing.

This is the appeal of synchronous delivery. Here's the rub: unless you plan to only allow a single observer per object, then you have to pick someone to go first. And unless you plan to prevent that person from further mutating the object, the next guy to be called may not observe the world in the state suggested by the "synchronous" call.

In fact, an arbitrary number of mutations can always have occurred by the time any given observer is called, so it's just dishonest API which pretends that a single thing has happened. The correct thing is to inform each observer what set of things has happened.

So the only questions are:

  1. Do you mislead the consumer with API which suggests that only one thing will has happened
  2. If not, when do you deliver the set of things: immediately after the mutation occurs, at the end of the turn or ask the UA to schedule a future task.

Referring back to my reason (1) This question was debated extensively with DOM Mutation Observers and unanimously decided that the end of the turn was the only good solution. "immediately" puts all code in danger of having its runtime assumptions invalidated after every operation which mutates objects and "future task" is far too late to be useful for most almost all use cases.

I can understand how batched changes and end-of-turn asynchronous calls are a good match. The development model is now "here are the changes from this turn" rather than "here is a change".

But I don't understand how end-of-turn asynchronous calls helps with the multiple independent listeners problem. If a listener can mutate the DOM, then change-listeners depend upon change-listeners. The relative timing of the listener calls and the non-listener DOM mutations does not change that dependency.

And once change-listeners mutate the DOM then the API of "here are the changes from this turn" again misleads the developer relying on it.

Actually, this isn't quite right. It's "here are all the changes since the last time you were invoked". I.e. every observer is always delivered all changes to all entities it is observing up to the time that it is invoked. To be concrete, the following invariants always hold:

A) When an observer is invoked, is it delivered a sequence of change records representing all changes to entities it is observing -- up to the time of its invocation. B) When an observer is invoked, it is free to mutate entities and do work without risk of being preempted by other observers being notified of changes it makes. C) Conversely, when an observer is invoked, no other script is below it on the stack. D) An observer will be delivered all changes to entities it is observing before the turn ends.

These are true for both Object.observe() and for DOM Mutation Observers.

In other words, any given observer can be blissfully ignorant of all other actors in the system. It is always delivered a full picture of what has happened in its world, it can act completely independently of other actors, and it will always get all of its changes before the turn ends.

Does this address your concern?

# John J Barton (13 years ago)

On Sun, Aug 19, 2012 at 3:31 PM, Rafael Weinstein <rafaelw at chromium.org>wrote:

On Sun, Aug 19, 2012 at 11:25 AM, John J Barton <johnjbarton at johnjbarton.com> wrote:

On Fri, Aug 17, 2012 at 9:49 PM, Rafael Weinstein <rafaelw at chromium.org> wrote:

A synchronous observation mechanism provides an attacker too many opportunities for a plan interference attack. If you'll recall, an earlier synchronous proposal died for this reason.

That is an excellent reason. I have two others:

  1. It's a terrible design pattern to encourage. Webdevs will absolutely use it and learn the hard way, just like we did with Mutation Events that it works fine when we start, but down the road lies nothing but pain and suffering.

  2. Synchronous doesn't actually exist -- it's a false option.

To explain: When people talk about "synchronous" what they expect is that they will be notified concurrent with the event happening (this is happening). The expectation is that when they are invoked they are observing the world as the event is describing.

This is the appeal of synchronous delivery. Here's the rub: unless you plan to only allow a single observer per object, then you have to pick someone to go first. And unless you plan to prevent that person from further mutating the object, the next guy to be called may not observe the world in the state suggested by the "synchronous" call.

In fact, an arbitrary number of mutations can always have occurred by the time any given observer is called, so it's just dishonest API which pretends that a single thing has happened. The correct thing is to inform each observer what set of things has happened.

So the only questions are:

  1. Do you mislead the consumer with API which suggests that only one thing will has happened
  2. If not, when do you deliver the set of things: immediately after the mutation occurs, at the end of the turn or ask the UA to schedule a future task.

Referring back to my reason (1) This question was debated extensively with DOM Mutation Observers and unanimously decided that the end of the turn was the only good solution. "immediately" puts all code in danger of having its runtime assumptions invalidated after every operation which mutates objects and "future task" is far too late to be useful for most almost all use cases.

I can understand how batched changes and end-of-turn asynchronous calls are a good match. The development model is now "here are the changes from this turn" rather than "here is a change".

But I don't understand how end-of-turn asynchronous calls helps with the multiple independent listeners problem. If a listener can mutate the DOM, then change-listeners depend upon change-listeners. The relative timing of the listener calls and the non-listener DOM mutations does not change that dependency.

And once change-listeners mutate the DOM then the API of "here are the changes from this turn" again misleads the developer relying on it.

Actually, this isn't quite right. It's "here are all the changes since the last time you were invoked". I.e. every observer is always delivered all changes to all entities it is observing up to the time that it is invoked. To be concrete, the following invariants always hold:

A) When an observer is invoked, is it delivered a sequence of change records representing all changes to entities it is observing -- up to the time of its invocation. B) When an observer is invoked, it is free to mutate entities and do work without risk of being preempted by other observers being notified of changes it makes. C) Conversely, when an observer is invoked, no other script is below it on the stack. D) An observer will be delivered all changes to entities it is observing before the turn ends.

These are true for both Object.observe() and for DOM Mutation Observers.

In other words, any given observer can be blissfully ignorant of all other actors in the system. It is always delivered a full picture of what has happened in its world, it can act completely independently of other actors, and it will always get all of its changes before the turn ends.

Does this address your concern?

Can we create a simple example that demonstrates the API advantage of asynchronous events in the multiple observer case?

The example would need a DOM mutations (M1) and two handlers (H1 and H2) and let's have H1 mutate the DOM (M2) in a way that H2 depends upon

Synchronous, H1 first: H1(M1) H2(M2) H2(M1) <<< correct? or H2(M1, M2)? Async, H1 first H1(M1) H2(M1) H2(M2)

Sync H2 first: H2(M1) H1(M1) H2(M2) Async H2(M1) H1(M1) H2(M2)

If I am understanding correctly (and did the example correctly ;-), then the async model fires all handlers with 1st order DOM mutations, then fires all handlers with mutations created by other handlers (possibly repeating).

In the sync model, 2nd order mutations-from-mutation-handlers have to be delivered before we finish the first round.

If I'm understanding correctly then the async model is clearer.

jjb

# Rafael Weinstein (13 years ago)

On Mon, Aug 20, 2012 at 8:57 PM, John J Barton <johnjbarton at johnjbarton.com> wrote:

On Sun, Aug 19, 2012 at 3:31 PM, Rafael Weinstein <rafaelw at chromium.org> wrote:

On Sun, Aug 19, 2012 at 11:25 AM, John J Barton <johnjbarton at johnjbarton.com> wrote:

On Fri, Aug 17, 2012 at 9:49 PM, Rafael Weinstein <rafaelw at chromium.org> wrote:

A synchronous observation mechanism provides an attacker too many opportunities for a plan interference attack. If you'll recall, an earlier synchronous proposal died for this reason.

That is an excellent reason. I have two others:

  1. It's a terrible design pattern to encourage. Webdevs will absolutely use it and learn the hard way, just like we did with Mutation Events that it works fine when we start, but down the road lies nothing but pain and suffering.

  2. Synchronous doesn't actually exist -- it's a false option.

To explain: When people talk about "synchronous" what they expect is that they will be notified concurrent with the event happening (this is happening). The expectation is that when they are invoked they are observing the world as the event is describing.

This is the appeal of synchronous delivery. Here's the rub: unless you plan to only allow a single observer per object, then you have to pick someone to go first. And unless you plan to prevent that person from further mutating the object, the next guy to be called may not observe the world in the state suggested by the "synchronous" call.

In fact, an arbitrary number of mutations can always have occurred by the time any given observer is called, so it's just dishonest API which pretends that a single thing has happened. The correct thing is to inform each observer what set of things has happened.

So the only questions are:

  1. Do you mislead the consumer with API which suggests that only one thing will has happened
  2. If not, when do you deliver the set of things: immediately after the mutation occurs, at the end of the turn or ask the UA to schedule a future task.

Referring back to my reason (1) This question was debated extensively with DOM Mutation Observers and unanimously decided that the end of the turn was the only good solution. "immediately" puts all code in danger of having its runtime assumptions invalidated after every operation which mutates objects and "future task" is far too late to be useful for most almost all use cases.

I can understand how batched changes and end-of-turn asynchronous calls are a good match. The development model is now "here are the changes from this turn" rather than "here is a change".

But I don't understand how end-of-turn asynchronous calls helps with the multiple independent listeners problem. If a listener can mutate the DOM, then change-listeners depend upon change-listeners. The relative timing of the listener calls and the non-listener DOM mutations does not change that dependency.

And once change-listeners mutate the DOM then the API of "here are the changes from this turn" again misleads the developer relying on it.

Actually, this isn't quite right. It's "here are all the changes since the last time you were invoked". I.e. every observer is always delivered all changes to all entities it is observing up to the time that it is invoked. To be concrete, the following invariants always hold:

A) When an observer is invoked, is it delivered a sequence of change records representing all changes to entities it is observing -- up to the time of its invocation. B) When an observer is invoked, it is free to mutate entities and do work without risk of being preempted by other observers being notified of changes it makes. C) Conversely, when an observer is invoked, no other script is below it on the stack. D) An observer will be delivered all changes to entities it is observing before the turn ends.

These are true for both Object.observe() and for DOM Mutation Observers.

In other words, any given observer can be blissfully ignorant of all other actors in the system. It is always delivered a full picture of what has happened in its world, it can act completely independently of other actors, and it will always get all of its changes before the turn ends.

Does this address your concern?

Can we create a simple example that demonstrates the API advantage of asynchronous events in the multiple observer case?

The example would need a DOM mutations (M1) and two handlers (H1 and H2) and let's have H1 mutate the DOM (M2) in a way that H2 depends upon

If: -I understand your notation -We use DOM Mutation Event's behavior as the model of synchronous -DOM Mutation Observers and/or Object.observe() as the model of async,

..then you have it basically right, but it's actually worse in the sync case and better in the async case than you've stated, as:

-In the H1-first case, H2 gets to handle both M1 and M2 in the same invocation, as those represent the changes up to that point -In the both cases, H1 gets notified of its own changes because in the sync model there's no good way to ignore your own changes. In the async model, it's trivial -- you just suspend observation while you do your work.

Synchronous, H1 first: H1(M1) H2(M2) H2(M1) <<< correct? or H2(M1, M2)?

H1(M1) H1(M2) H2(M2) H2(M1) <<< yes, but H1 has to tolerate being notified of his own change.

Async, H1 first H1(M1) H2(M1) H2(M2)

H1(M1) H2(M1, M2)

Sync H2 first: H2(M1) H1(M1) H2(M2)

H2(M1) H1(M1) H1(M2) H2(M2)

Async H2(M1) H1(M1) H2(M2)

correct

# Steve Sanderson (13 years ago)

This is fantastic - thanks for pushing this forwards, Rafael!

For context, I'm part of the knockout.js (a JavaScript MV* library) team, so I have some interest in how this could work out. This spec has a lot of potential to make MVC-style coding far simpler and more robust, both for framework developers and web app developers. Although the semantics of the Object.observe proposal are different in a few ways from what Knockout and similar frameworks use for observability, it certainly looks applicable enough to the same design patterns and APIs.

There's one significant extra aspect of observability that Knockout, Batman, CanJS, and other MVC-type frameworks rely on that isn't covered as far as I can tell by the proposal so far. These frameworks can automatically determine the dependencies of an arbitrary block of code. For example, if there is a block of code that determines the output of a computed property, like this:

function fullName() { return firstName + " " + lastName };

... then if you use such a function during a declarative binding, those frameworks' observability mechanisms will automatically know which underlying observable properties it depends on. This is possible because the frameworks' observabilty mechanisms can notify on read as well as on write. This ends up being a very powerful and flexible feature whereby the framework can manage arbitrary and dynamically-changing dependency chains, and the developer can robustly call arbitrary custom functions in computed property evaluators and bindings.

These facilities could be made possible in the Object.observe proposal by adding one further primitive feature. Just as you can register for notifications on write, you'd ideally also be able to register for separate notifications on read. Typically only framework code would do that - application code wouldn't usually need to. The actual dependency detection logic would differ depending on whether read notifications were delivered synchronously or asynchronously, but then everything else can be built on top of that primitive.

Please let me know if you want more detailed descriptions of how any of this works in Knockout/etc. I'd be happy to suggest how a comparable API might look in the Object.observe world!

Steve

# Rick Waldron (13 years ago)

On Wednesday, August 22, 2012 at 6:34 AM, Steve Sanderson wrote:

This is fantastic - thanks for pushing this forwards, Rafael!

For context, I'm part of the knockout.js (a JavaScript MV* library) team, so I have some interest in how this could work out. This spec has a lot of potential to make MVC-style coding far simpler and more robust, both for framework developers and web app developers. Although the semantics of the Object.observe proposal are different in a few ways from what Knockout and similar frameworks use for observability, it certainly looks applicable enough to the same design patterns and APIs.

There's one significant extra aspect of observability that Knockout, Batman, CanJS, and other MVC-type frameworks rely on that isn't covered as far as I can tell by the proposal so far. These frameworks can automatically determine the dependencies of an arbitrary block of code. For example, if there is a block of code that determines the output of a computed property, like this:

function fullName() { return firstName + " " + lastName };

... then if you use such a function during a declarative binding, those frameworks' observability mechanisms will automatically know which underlying observable properties it depends on. This is possible because the frameworks' observabilty mechanisms can notify on read as well as on write.

Couldn't this be accomplished via get accessor and firing a notification from within the accessor function when properties are read?

# Rafael Weinstein (13 years ago)

Thanks for joining the conversation. =-)

The automatic dependency discovery that Knockout does is rather cool. My sense has been that Proxy is a better tool for this job than is expanding the semantics of Object.observe() to include reads.

Object.observe() only operates on objects which are specifically observed, so something like

get val function() { this.foo.bar + this.bat.baz; }

Would only report access to this.foo and this.bat, but not foo.bar and bat.baz.

While applying the function to a proxy wrapper allows for the creation of a "membrane" through which you can discover dependencies not available on the local object.

Is my thinking flawed?

Note that the Object.observe() specifically reports when properties are reconfigured such that any approach you take to computed properties can know when it needs to invoke its strategy and when it can rely on the system-generated change records.

# Brandon Benvie (13 years ago)

I would say it is most definitely not the concern of Observe to watch reads and between accessors and Proxies we have all the tools we need for that. With the ability to keep present on notifications of changes via observe it's actually possible to implement a mirror that can be emit change events (by explicitly mirroring changes on the original target changes) and provide the benefits of the observe api to listeners, and then implement any kind of additional tracking on top of that either by using accessors or being a proxy.

# Alex Russell (13 years ago)

On Thu, Aug 23, 2012 at 7:06 AM, Brandon Benvie <brandon at brandonbenvie.com>wrote:

I would say it is most definitely not the concern of Observe to watch reads and between accessors and Proxies we have all the tools we need for that.

I think that misreads the situation. Having proxies available might work for this, but is it the right (implied) UI for it? I.e., if you need to do half of your work with observe() and then pivot over to proxies for the other half...that strikes me as strange. There might be good reasons not to, but saying "you can do it with what we've got" is always tautological in a turing machine = )

# François REMY (13 years ago)

Here’s my take on the binding thing: fremycompany.com/BG/2012/ECMAScript-Binding-Manager-951

Key features:

  • Do not require to have a reference on an object to bind to it (binding is implicit and managed by the browser).
  • Accessors, function calls and inner dependencies are managed automatically.
  • Since the browser manage most the binding wiring, it can makes tons of optimizations.
  • It’s trivial to use for developers.

Have a look ;-)

From: Alex Russell Sent: Thursday, August 23, 2012 11:47 AM To: Brandon Benvie Cc: steven at stevensanderson.com ; es-discuss at mozilla.org Subject: Re: Re: Experimental implementation of Object.observe & JS Utilitylibrary now available On Thu, Aug 23, 2012 at 7:06 AM, Brandon Benvie <brandon at brandonbenvie.com> wrote:

I would say it is most definitely not the concern of Observe to watch reads and between accessors and Proxies we have all the tools we need for that.

I think that misreads the situation. Having proxies available might work for this, but is it the right (implied) UI for it? I.e., if you need to do half of your work with observe() and then pivot over to proxies for the other half...that strikes me as strange. There might be good reasons not to, but saying "you can do it with what we've got" is always tautological in a turing machine = )

With the ability to keep present on notifications of changes via observe it's actually possible to implement a mirror that can be emit change events (by explicitly mirroring changes on the original target changes) and provide the benefits of the observe api to listeners, and then implement any kind of additional tracking on top of that either by using accessors or being a proxy.


es-discuss mailing list es-discuss at mozilla.org, mail.mozilla.org/listinfo/es

# Andrea Giammarchi (13 years ago)

Sorry guys, I just wonder if there's any outcome here ... rather than keep proposing something else :-)

# Rafael Weinstein (13 years ago)

ois,

Thanks so much for your thoughtful consideration of the Object.observe() proposal. Here's my take on your counter-proposal:

Interestingly, what you've focused on is the one thing we intentionally left out of the Object.observe() proposal, namely: observing computed properties. In that way, I don't see what you've proposed as an alternative, but rather supplementary.

First, I'd like to point out that, while most people these days seem to say "databinding" and specifically mean updating a UI, there are important use cases which depend on observing mutations to objects which are entirely distinct.

Note that what you've proposed, doesn't include the following abilities:

  1. Discovering when new properties are added to objects -- which is particularly important in the case of Arrays, where you often want to bind to the set of elements in an Array

  2. Knowing the order of changes which occurred -- this is important to many use cases, including persisting changes to domain objects and efficiently computing effective changes to complex data structures (a DOM tree implemented in JS would be one example).

  3. Discovering when properties have been reconfigured -- which is important if your strategy for observing computed properties is comparatively expensive, and you need to know when to employ it.

  4. Generally, having full knowledge of what happened to an object so as to able to efficiently mirror it -- which is important in some synchronization strategies.

Ok, with that out of the way, I think the topic you've raised: computed properties, deserves its own thread -- so I'll start a new thread for that purpose and put my thoughts there.

# Steve Sanderson (13 years ago)

I do like François's notion of "Object.makeBindable". It would appear to tie in with Object.observe to produce a solid, complete mechanism for observability with dependency detection. I'll follow up in more detail in the new thread.

# Rick Waldron (13 years ago)

On Wed, Aug 29, 2012 at 6:09 AM, Steve Sanderson <flares at gmail.com> wrote:

I do like François's notion of "Object.makeBindable". It would appear to tie in with Object.observe to produce a solid, complete mechanism for observability with dependency detection. I'll follow up in more detail in the new thread.

Nit: "Object.makeBindable" sounds like: "prepare this for bindability", but "bind" is already a concept in the language, where Function.prototype.bind creates a new function object from an existing function object, but with a BoundThis as specified by the thisArg in fn.bind( thisArg );

... That's a point of confusion that we can easily avoid by not reusing terminology.

# François REMY (13 years ago)

From: Rick Waldron

Nit: "Object.makeBindable" sounds like: "prepare this for bindability", but "bind" is already a concept in the language, where Function.prototype.bind creates a new function object from an existing function object, but with a BoundThis as specified by the thisArg in fn.bind( thisArg );

You've a point. Object.makeObservable() may be better, indeed.

# Andrea Giammarchi (13 years ago)

+1 for makeObservable