Feedback on "Read-only Override Prohibition is [not a mistake]"

David Bruant bruant.d at gmail.com
Sun Jan 15 12:23:04 PST 2012


Le 15/01/2012 20:21, Allen Wirfs-Brock a écrit :
> 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.
Here, I used Object.defineProperty, only to illustrate the use of some
mechanism that allows the creation of an own read-only property (and not
as the standard Object.defineProperty function), sorry for the confusion.
Such a mecanism was introduced in ES5 to give authors the ability to
define things like the "length" property of a function.

So, in the design of ES5 was planned to add something that offer the
capacity to create own properties with fine-grained control. You can
replace the above Object.defineProperty call by any other mecanism that
would have allowed to dynamically (after object initialization) add an
own read-only property since that was a feature that was planned to be
added to the language in a way or another regardless of design details
(like how the property works when inherited).


>>> 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.
What is a real world ECMAScript 3 case of inheriting a read-only property?
The only code I can come up with is:
-----
function C(){
    this.length = 3;
}

function f(a,b){}

C.prototype = f;

var c = new C();
console.log(c.length);
-----
For this code, ECMAScript 5 says:
When doing "new C()", |this| is assigned "C.prototype" which is the
function f (it has a non-writable, non-configurable "length" property
with 2 as value).
During "this.length = 3;", [[CanPut]] should return
"inherited.[[Writable]]" (step 8.b). In this case, it should return false.

Firefox 9, 11 (Aurora) and Chrome 16 log 3 (non-compliant)
Opera 11.60 and 12Alpha log 2 (ES5 compliant)
Could anyone try with other browsers and post the results, please.
So it seems that some major browsers are not conformant to begin with.
Interesting.

Can you think of other cases that predate ES5 where browsers acted
according to the spec with regard to inherited non-writable properties?


>
>> 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.
----
var o = Object.create(Object.freeze({a:1}));
o.a = 2;
console.log(o.a); // 1 in Firefox (spec compliant), 2 in Chrome
----
Mark mentionned in the strawman, that it would be 2 in Safari/JSC as
well (I can't test now). I haven't heard of anyone neither using this
intensively nor even noticing the difference.
I fully agree with the goal of preventing inconsistence between the spec
and code on the web, but 2 major browsers already have inconsistent
behaviors and few seems to have noticed.

Moreover, as I showed above, some major browsers are non-compliant with
the spec. Since how long has it been the case?


>> 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.
What would be the point of duplicating if there is already inheritance.
Doesn't inheritance exist to avoid such duplication?

> 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. 
Good to know. Thanks.

David
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20120115/c9bbe760/attachment.html>


More information about the es-discuss mailing list