Deleting Getters and Setters (was Re: Controlling DontEnum...)
-----Original Message----- From: Kris Zyp [mailto:kris at sitepen.com] Sent: 13. mars 2008 18:14 To: Lars Hansen; Mark S. Miller Cc: es4-discuss at mozilla.org Subject: Deleting Getters and Setters (was Re: Controlling DontEnum...)
the fact; getters and setters defined by an object initializer or in
a
class are fixtures and hence not deletable.
Really, that doesn't create a compatibility problem? I realize getters and setters aren't in ES3, but this seems like ES4 would be making a significant departure in behavior from the majority of current browser implementations of the same getter/setter syntax used by ES4:
obj = {get foo() { return 'hi' }} Object foo=hi
obj.foo "hi"
delete obj.foo true
obj.foo = 'goodbye' "goodbye"
obj.foo "goodbye"
With ES4, obj.foo would still be returning "hi" at the end? That sounds nice, but is there no fear of compatibility issues with that?
It's an interesting point. The proposal is here: proposals:getters_and_setters but does not mention deletion (either way).
Brendan, opinions?
On Mar 17, 2008, at 1:31 PM, Lars Hansen wrote:
-----Original Message----- From: Kris Zyp [mailto:kris at sitepen.com]
obj = {get foo() { return 'hi' }} Object foo=hi
obj.foo "hi"
delete obj.foo true
obj.foo = 'goodbye' "goodbye"
obj.foo "goodbye"
With ES4, obj.foo would still be returning "hi" at the end? That sounds nice, but is there no fear of compatibility issues with that?
It's an interesting point. The proposal is here: proposals:getters_and_setters but does not mention deletion (either way).
Brendan, opinions?
Clearly the proposal is incomplete :-/. SpiderMonkey originated three
special forms for getters and setters, in 1999 (see rev 3.18 at
bonsai.mozilla.org/cvsgraph.cgi?file=/mozilla/js/src/jsparse.c
for one entry point into the change):
- getter function x() { return ++i; };
- o = {x getter: function () { return ++this.i; }};
- o = {i: 0}; o.x getter= function () { return ++this.i; };
The first two were changed long ago, with Waldemar guiding things,
into the ES4 syntax the RI supports today:
1'. function get x() { return ++i; }; 2'. o = {get x() { return ++this.i; };
SpiderMonkey supports only form 2', while still maintaining support
for the original 1-3.
Form 3 became
3': o = {i: 0}; o.defineGetter('x', function () { return + +this.i; });
About DontDelete: only the first form or its revised version, 1 or
1', sets DontDelete among the bound getter's attributes, just as any
non-eval'ed function binding created for a function definition makes
a DontDelete property (eval makes delete-able bindings, for reasons I
do not recall from ES1 days -- this is design flaw, btw, but I won't
get into it here).
ES4 supports get and set methods in classes, also DontDelete or
stronger (fixtures, so at least DontDelete).
For backward compatibility with SpiderMonkey, one might like 2' to
make a getter that can be deleted. On the other hand, for higher
integrity one might prefer that the object-initialiser-hosted
syntactic form would make a DontDelete property.
The recent o = {var x: "don't delete"} proposal compatibly extends
initialiser syntax to support user-created DontDelete properties, but
whatever the outcome for that late-breaking micro-proposal, I do not
think o = {var get x() { ... }}; is a good idea -- var and get next
to each another do not convey DontDelete; the combo looks like a typo
of some sort.
And would const get for a ReadOnly + DontDelete binding be wanted
too? A getter without a setter in SpiderMonkey behaves like a const
binding, in that assignments go into the bit bucket:
js> o = {get x() ++this.i, i:0} [object Object] js> o.x
1 js> o.x
2 js> o.x = 42
42 js> o.x
3
A getter without a setter makes something const-like, in the sense of
ES1-3's ReadOnly attribute and its semantics, but this kind of const
lacks "defensive consistency", to use a phrase defined in Mark
Miller's thesis. We used to throw an error in SpiderMonkey when one
sets a getter-only property, but we changed this to match the desired
(for "foolish consistency"? ;-) ES1-3-like ReadOnly behavior. Users
wanted only one kind of const.
A setter without a getter means property gets return undefined. We've
always done this.
I hope this helps, even though it is about more than DontDelete.
Given our history and the delete-ability of properties created in
object initialisers in ES3, I favor reserving DontDelete getters and
setters for the 1' special form: a variation on function definition
syntax. So only
function get x() ++i;
or similar in a class (static or instance) would make a DontDelete
binding, if not a fixture. My two cents,
On Mar 17, 2008, at 2:36 PM, Brendan Eich wrote:
- getter function x() { return ++i; };
- o = {x getter: function () { return ++this.i; }};
- o = {i: 0}; o.x getter= function () { return ++this.i; };
The first two were changed long ago, with Waldemar guiding things, into the ES4 syntax the RI supports today:
1'. function get x() { return ++i; }; 2'. o = {get x() { return ++this.i; };
SpiderMonkey supports only form 2', while still maintaining support for the original 1-3.
Form 3 became
3': o = {i: 0}; o.defineGetter('x', function () { return + +this.i; });
Only in SpiderMonkey, I should have added -- the RI and of course ES4
do not specify support for define[GS]etter.
On Mon, Mar 17, 2008 at 5:36 PM, Brendan Eich <brendan at mozilla.org> wrote:
For backward compatibility with SpiderMonkey, one might like 2' to make a getter that can be deleted.
That pattern is used in Firefox now (and perhaps elsewhere) to do lazy initialization of expensive objects:
this.defineGetter("Debug", function() { var d = loadModule("debug.js"); delete this.Debug; this.Debug = d; return d; });
We could adapt, obviously, dunno if it's widely used on the web.
Mike
On Mar 17, 2008, at 3:08 PM, Mike Shaver wrote:
On Mon, Mar 17, 2008 at 5:36 PM, Brendan Eich <brendan at mozilla.org>
wrote:For backward compatibility with SpiderMonkey, one might like 2' to make a getter that can be deleted.
That pattern is used in Firefox now (and perhaps elsewhere) to do lazy initialization of expensive objects:
this.defineGetter("Debug", function() { var d = loadModule("debug.js"); delete this.Debug; this.Debug = d; return d; });
We could adapt, obviously, dunno if it's widely used on the web.
With only SpiderMonkey supporting defineGetter, it's not widely
used. The canonical JS memoization pattern, presented nicely here:
osteele.com/archives/2006/04/javascript-memoization
changes (shadows or replaces) a function-valued property, requiring
callers to add pesky () to invoke the property explicitly. With
getters and setters, this noise-problem can be avoided and (beyond
noise reduction) old client code that wanted to get a property, not
call a method, can be made to call a memoizing wrapper without
requiring vast or infeasible (you don't own write access to the
client code) search and replace work.
So getters and setters plus memoization patterns do summon
defineGetter, in our experience.
On Mon, Mar 17, 2008 at 6:14 PM, Brendan Eich <brendan at mozilla.org> wrote:
With only SpiderMonkey supporting defineGetter, it's not widely used. The canonical JS memoization pattern, presented nicely here:
Yeah, I would expect that only properties of new objects are lazily defined that way, using the 2' form from above.
Mike
syntax. So only
function get x() ++i;
or similar in a class (static or instance) would make a DontDelete binding, if not a fixture. My two cents,
That an object literal can make a getter that can be deleted, but never restored, makes for an odd bit of assymetry doesn't it? A getter can be deleted, but not added. But, I agree that a getter generated from an object literal should probably still be deletable for compatibility. Too bad there isn't a way to add getters to an existing object to restore the symmetry. Kris
Really, that doesn't create a compatibility problem? I realize getters and setters aren't in ES3, but this seems like ES4 would be making a significant departure in behavior from the majority of current browser implementations of the same getter/setter syntax used by ES4:
Object foo=hi
true
With ES4, obj.foo would still be returning "hi" at the end? That sounds nice, but is there no fear of compatibility issues with that? Kris