The way too handle readonly properties in the prototype.
Mark S. Miller
erights at google.com
Sat Sep 17 15:32:52 PDT 2011
On Sat, Sep 17, 2011 at 10:14 AM, Xavier MONTILLET
<xavierm02.net at gmail.com>wrote:
>
> When I say Firefox says, you can't I mean you really can't:
>
> ( function ( ) {
> 'use strict';
> function Constructor ( ) { }
> Object.defineProperty( Constructor.prototype, 'property', {
> value: 'prototype',
> writable: false
> } );
> var object = new Constructor( );
> object.property = 'instance';// Error: 'object.property is read-only'
> (in Firefox)
> } )( );
>
> I'm quite sure this is a bug.
Yes, as Brendan says, ES5.1 demands that it throw a TypeError.
> But I find this behavior interesting.
> Defining properties of the prototype with Object.defineProperty lets
> you prevent other instances or anything else from modifying common
> methods.
>
Hi Xavier, despite appearances, this is not a protection mechanism and in
fact provides no protection. The following returns 'instance' on Firefox, as
it must according to the ES5.1 spec:
( function ( ) {
'use strict';
function Constructor ( ) { }
Object.defineProperty( Constructor.prototype, 'property', {
value: 'prototype',
writable: false
} );
var object = new Constructor( );
Object.defineProperty(object, 'property', {value: 'instance'});
return object.property;
} )( );
The behavior is there for compatibility with ES3, not for protection. In
fact, from a protection perspective, this behavior is quite unfortunate. It
means that if you lock down your primordial prototypes naively, as SES
currently does during its initialization, to prevent prototype poisoning and
the implicit sharing of prototypes from being a communications channel, then
legacy programs that do not monkey patching of primordial prototypes, as so
should be SES-compatible, nevertheless fail to override inherited methods by
simple assignment. This incompatibility is only an irritation, since it
provides no protection.
Fortunately, there is a less naive way to lock down the primordial
prototypes that don't create this incompatibility. SES will move to that
soon.
> But most of the time, at least for methods, you want them to remain
> the same for the life of the object.
> Here is an example:
> Let's say you want to prevent anything stupid from happening to the
> methods you need:
>
> function Constructor( ) { }
> Object.defineProperty( Constructor.prototype, 'method', {
> value: function ( ) { },
> writable: false
> } );
>
> Now, your instances are "safe" since their method can't be removed.
> But sometimes, you don't even want the person using the object to be
> able to set another next method for you object.
> You could prevent him from doing so by doing this:
>
> function Constructor ( ) {
> Object.defineProperty( this, 'method', {
> value: function ( ) { },
> writable: false
> } );
> }
>
> but then, your methods won't be shared between instances.
>
This is the right thing surprisingly often, although it does cause an extra
allocation per method per instance on most modern JS engines. Although each
instance gets its own method object, all these method objects still do share
code. So it depends on why you want sharing between instances.
If what you want is to place a protected method on the prototype and prevent
anyone outside the abstraction from overriding this method on the instance,
you can do this without the extra allocation by having the constructor seal,
freeze, or preventExtensions on the instance, depending on what kind of
protection you want. Note that you still can't prevent overriding by
"external" inheritance, i.e., inheritance outside the abstraction:
var naughty = Object.create(new Constructor(), {
method: { value: function myNaughtyOverrideMethod() {...}}
});
If you then invoke naughty.foo(), where foo is inherited from
Constructor.prototype that does a "this.method()", the .method() that foo()
will invoke is your naughty override.
The overall lesson is that it is tricky, but not impossible, to write
defensive code using "this" in a secured JavaScript. If you avoid "this" in
your code, as a simple variant of your method-per-instance pattern can, then
it becomes vastly easier to write defensive code in a secured JS.
> ----------------------------------------------------------
>
> To put it in a nuttshell:
> - Is it the normal behavior that if a property is protected on the
> prototype, you can't set it on the object?
>
Using assignment, you can't. Using Object.defineProperty, you can, so long
as the object is still extensible. Thus it is a legacy compatibility
irritant, rather than something offering any protection.
> - I think it is a behavior that could be used in some cases, even
> though it isn't suited as default behavior.
>
I would enjoy seeing an example where this half-protection is actually
useful.
--
Cheers,
--MarkM
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20110917/24dd89f9/attachment-0001.html>
More information about the es-discuss
mailing list