On object extension
First, if obj already has a non-configurable property 'a', this will
throw
I don't think object extension on a non configurable properties should be a no op.
Your basically saying "extend obj with { a: 1 } unless doing so would throw an error, in which case fail silently"
Personally I don't like silent failure points.
Another feature which could be handy is extending an object with another
already existing object (aka mixins?).
Having some kind build in API to mixin / extend objects would indeed be helpful
Le 28/11/2011 21:00, Jake Verbaten a écrit :
First, if obj already has a non-configurable property 'a', this will throw
I don't think object extension on a non configurable properties should be a no op.
It was just an example. I explained later that the setting should happen only when the existing property is either configurable or writable and that if a property exists and the value is overridden, its attributes should be kept constant. Current proposal would turn a configurable non-writable property into a writable property which is not a silent error, but rather a big mistake.
Your basically saying "extend obj with { a: 1 } unless doing so would throw an error, in which case fail silently"
Personally I don't like silent failure points.
I don't see it as a failure. I want to avoid to throw an error when the property is non-writable and non-configurable. The problem with a syntax-based operator is that it is written once for all.
function addABC(obj){ obj.{a:1, b:2, c:3}; }
Should this function throw whenever the extension is not possible (a property is non-writable & non-configurable) as written or do its best to extend the object. The problem with the former is that to ensure that the assignment won't fail, one need to check that for all properties, there is no such existing properties that is non-configurable and non-writable (which is what i propose to do implicitly).
On Nov 28, 2011, at 1:02 PM, David Bruant wrote:
Current proposal would turn a configurable non-writable property into a writable property which is not a silent error, but rather a big mistake.
A configurable non-writable property is a big mistake. Such a setting has zero integrity, it's "advisory const". You might argue for it in a setting where such advice can be ignored, so the attribute combination is not illegal. But it's nonsensical to argue that some Object.defineProperty variant shouldn't be able to reconfigure to writable such a property.
Whether .{ should be such a variant is of course debatable.
Your basically saying "extend obj with { a: 1 } unless doing so would throw an error, in which case fail silently"
Personally I don't like silent failure points. I don't see it as a failure. I want to avoid to throw an error when the property is non-writable and non-configurable.
Why?
The problem with a syntax-based operator is that it is written once for all.
function addABC(obj){ obj.{a:1, b:2, c:3}; }
Should this function throw whenever the extension is not possible (a property is non-writable & non-configurable) as written or do its best to extend the object. The problem with the former is that to ensure that the assignment won't fail, one need to check that for all properties, there is no such existing properties that is non-configurable and non-writable (which is what i propose to do implicitly).
So don't check, let the program fail.
The problem with the latter (i.e., with "[doing] its best to extend the object" is that you could end up with a bad combination of old and new properties.
There's no silver bullet here. If we want something (syntax TBD) as a special form for "extend this object with these literal properties", we as programmers have to choose a policy. JS standardizers must agree on the default policy.
This is tricky but simpler is better. People have been using PrototypeJS's extend for ages, but in a world with no real non-configurable and/or non-writable properties on user-defined objects. So Prototype's extend doesn't shed much light on the future.
The traitsjs.org work is a bit freeze-happy and closure-costly, but it can be used today. Are people using it? I recall Bespin (now Cloud9) tried it but the closure costs (per method per instance) were too high.
Choosing default conflict policy before we have experience is a mistake, but if we must choose now, I think throwing is the only sane answer. We can relax it (via new "knobs" to twist) later if need be.
On Nov 29, 2011, at 3:58 PM, Brendan Eich wrote:
... So don't check, let the program fail.
The problem with the latter (i.e., with "[doing] its best to extend the object" is that you could end up with a bad combination of old and new properties.
There's no silver bullet here. If we want something (syntax TBD) as a special form for "extend this object with these literal properties", we as programmers have to choose a policy. JS standardizers must agree on the default policy.
This is tricky but simpler is better. People have been using PrototypeJS's extend for ages, but in a world with no real non-configurable and/or non-writable properties on user-defined objects. So Prototype's extend doesn't shed much light on the future.
The traitsjs.org work is a bit freeze-happy and closure-costly, but it can be used today. Are people using it? I recall Bespin (now Cloud9) tried it but the closure costs (per method per instance) were too high.
Choosing default conflict policy before we have experience is a mistake, but if we must choose now, I think throwing is the only sane answer. We can relax it (via new "knobs" to twist) later if need be.
/be
I really don't have much to add to what Brendan said. I spec'ed it the way I did because it is better to consistently reuse an existing policy than to make up a new policy for each new situation. In practice, I expect such extensions to be used in a manner such that they will seldom fail. If you are merging properties into an arbitrary and unknown shaped object then you probably need to define your own policy and do you own case analysis so .{ } is probably not what you need to use in that case. Neither is Object.defineProperties.
On Mon, Nov 28, 2011 at 9:51 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote: [...]
it is better to consistently reuse an existing policy than to make up a new policy for each new situation.
For me, this is the overriding consideration. ES5 defines two ways to set a property: assignment and Object.defineProperty. Both have a complex set of rules that need to be learned. Arguably some of these rules are non-sensical[1], but they are now in stone. Given that we introduce new syntax for setting properties, I strongly prefer that their semantics simply be one of these two, even if suboptimal for the use case expected of this new syntax, rather than invent yet a third set of rules for developers to learn.
That said, because of the similarity of ".{" to object literals, I think their semantics should simply be defined in the obvious way in terms of Object.defineProperty.
[1] I have come to hate the rule that you can't override an inherited non-writable data property by assignment. But I believe leaving this mistake alone is better than the consequences of any attempt to fix it.
The current proposal for object extension [1] works like the following (quoting the wiki):
obj.{a:1,b:2,c:3};
// is equivalent to
Object.defineProperties(obj, { a: {value: 1, enumerable: true, writable: true, configurable: true}, b: {value: 2, enumerable: true, writable: true, configurable: true}, c: {value: 3, enumerable: true, writable: true, configurable: true} };
It is an interesting improvement from ES5.1, but has some issues. First, if obj already has a non-configurable property 'a', this will throw (not to mention if obj is non-extensible). And unfortunately, doing something like:
if(Object.getOwnPropertyDescriptor(obj, 'a').configurable){ obj.{a:1, b:2, c:3}; } else{ obj.{b:2, c:3}; }
does not scale at all.
What about taking a more conservative approach and only add properties when either they don't exist or they exist and are configurable or writable? Something more like:
obj.{a:1,b:2,c:3};
// equivalent to
obj;
(function(){ var tmp = {a:1,b:2,c:3}; var names = Object.getOwnPropertyNames(tmp); names.forEach(function(n){ var desc = Object.getOwnPropertyDescriptor(obj, n); if(!desc){ Object.defineProperty(obj, n, {enumerable:true, configurable:true, writable:true, value: tmp[n]}); } else{ if(desc.configurable || desc.writable){ Object.defineProperty(obj, n, {value: tmp[n]}); // other attributes absent to keep existing values } else{ // Nothing. It's a constant! } } });
})();
I can think of any case where this would throw. Moreover, if one really cares about the state of a given property before and after the extension, she can inspect the object.
Another feature which could be handy is extending an object with another already existing object (aka mixins?). This feature could be more straightforward, because there is a way for the author to filter out properties before the extension (unlike with a syntax-based feature as I showed above)
David
[1] harmony:object_literals [2] nodejs.org/docs/v0.4.5/api/http.html#http.request