On Jan 15, 2012, at 8:10 AM, David Bruant wrote:

> Hi,
> 
> I'm refering to Allen's writing at [1]:
>> 
>> The basic idea is that the properties prototype object are shared
>> parts of all of inheriting child object. Modifying such a shared part
>> by a child, introduces a local change that is visible to that child
>> (and its children) so this requires creation of a “own” property on
>> the child. However, read-only properties can not modified (by normal
>> means, eg assignment) so there is no need to create a “own” copy.
>> 
> I agree with what you describe as an intention, but I don't know to what
> extent it applies to JavaScript (I don't know Self unfortunately):
> -----
> var p = {};
> var o = Object.create(p);
> var o2 = Object.create(p);
> o.a = 1; // creating an own property
> 
> Object.defineProperty(p, 'a', {value:2, configurable:false,
> writable:false});
> // now, o and o2 "share" an 'a' property. But do they really?
> -----
> The dynamicity of JavaScript makes the notion of "sharing" dynamic as
> well, I think.

However, the above requires Object.defineProperty which did not exist prior to 
ES5.  Since the starting point of this conversations regards whether or a 
design "mistake" was made in the ES5, I don't think it is valid to use 
defineProperty in trying to understand the original (prior to ES5) design 
intent.

> 
>> Assigning to an inherited read-only property or a “own” read-only
>> property should have the same affect (whether it is ignoring the
>> assignment, throwing, etc.).
>> 
> -----
> // following above code
> o.a = 3;
> o2.a = 4;
> -----
> Due to o having an 'a' property before inheriting from a read-only one,
> the notion of "same effect" cannot be applied (unless breaking other
> invariants).
> 
> 
>> Allowing assignment to an inherited read-only property would break the
>> invariant that that a prototype’s readonly property is an immutable
>> value that is shared among all children of the prototype.
>> 
>> If there was a mistake in designing ES5, it was allowing
>> Object.defineOwnProperty to create child properties that over-ride
>> inherited read-only data properties.This broke an invariant that
>> previously existed in the language but this invariant was already
>> violated by some pre-ES5 clause 15 objects, (eg the writability of the
>> prototype property of some children of Function.prototype). However, I
>> think the ES5 decision was probably the right one given the legacy
>> clause 15 usages and the overall reflective nature of defineOwnProperty).
>> 
> If I sum up, the design of Object.defineProperty was necessary for
> backward compatibility (pre-ES5) reasons. A consequence of this design
> is breaking the invariant you mentionned ("a prototype’s readonly
> property is an immutable value that is shared among all children of the
> prototype").

It was already broken in ES3 for some library objects.  However, the invariant 
still held in ES3 for all user defined objects. Object.defineProperty allows ES 
programmer to "break" the invariant in the same manner that ES implementors had 
previous been allowed to.  However, ES5 maintained the exact same semantics for 
assignment to an inherited read-only property as had existed in ES1-3.

> Considering this invariant is broken (as i showed above, it was probably
> doomed to be broken anyway), is there still a reason to not align
> [[CanPut]] as Mark suggests?

TC39(and the web) has a low tolerance for changes that will break existing 
code. In general, TC39 tries to avoid changes to the observable semantics of 
the language as defined by previous versions unless those changes are tied to 
new syntactic forms or some other type of explicit opt-in.  This was true for 
ES5 and remains true today.  Changing the semantics of assignment to an 
inherited read-only property would be such a change. It wasn't made in E5 and 
it would seem even harder to justify the change for ES6, given that ES5 made it 
easier to observe the semantics.

> 
> For the record, at the last JSConf, someone showed me code he wrote
> where he was declaring data attributes on the prototype with 'null' as
> value. It was the only way for his analysis tool to understand that
> "child instances" had such and such property. Such a practice prevents
> freezing the prototype (because the constructor would fail to assign
> values (unless using Object.defineProperty)).
> I don't know whether any part of this experience is good or bad, but I
> think it's worth noting that "declaring" things on the prototype is a
> practice that exists.

Yes, of course.  And in ES5, using Object.defineProperty in the instance 
constructor is probably the right way to deal with this idiom.

But the above can also be viewed as an existence proof that the existing set of 
attributes (plus the extensible flag) is insufficient to express all reasonable 
usage patterns.  Rather than changing the semantics associated with an existing 
attribute to enable a new pattern (while disabling other currently support 
patterns) another approach to consider is the addition of new but backwards 
compatible attributes that enable new semantics.  For example, you can imagine 
a "duplicate in children" attributes or a ""writable via over-ride" attribute 
that would address such scenarios.

In ES5 we avoided new attributes by evolving dontDelete into configurable and 
in ES6 we want to minimize additional primitive object model changes.  However, 
if there are important use cases that need to be supported then new attribute 
combinations seem like a plausible alternative to changing existing semantics. 

Allen


_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to