Setting a property in the prototype chain?
On 10.04.2011 21:24, Axel Rauschmayer wrote:
As far as I am aware, there is no way to change a property that isn’t at the beginning of the property chain, because the change will create a new property there.
Are there plans to change this? It would be nice to have something akin to class methods (without accessing the prototype via some other, e.g. global, reference).
Inherited accessor property can be change normally via assignment. Also
Object.defineProperty
is a way.
Dmitry.
Le 10/04/2011 19:24, Axel Rauschmayer a écrit :
As far as I am aware, there is no way to change a property that isn’t at the beginning of the property chain, because the change will create a new property there.
Are there plans to change this? It would be nice to have something akin to class methods (without accessing the prototype via some other, e.g. global, reference).
The ES5 Object.* API already provides everything you need to manipulate all properties you can find in an object (own or inherited). The "lowest-level" method to change the value of an own property is Object.defineProperty. This method can also be used to "re-configure" a property (make it non-configurable, change its enumerablity, change its "type" (data/accessor), change its writability if it is described by a data property descriptor). For examples, I recommend reading the MDN doc [1]. In order to access a reference to the prototype of an object, you can use Object.getPrototypeOf. Using both, you have enough power to achieve any change you could think of on any property of an object (own or inherited). Few platforms implement both at the moment. See [2] for reference. As Dmitry said, inherited accessor properties could be enough for what you need.
David
[1] developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty [2] kangax.github.com/es5-compat-table
function getDefiningObject(obj, key) { if (!(key in obj)) throw new Error("key " + key + " not found"); while (!obj.hasOwnProperty(key)) obj = Object.getPrototypeOf(obj); return obj; }
Exactly what I was looking for. Thanks.
As far as I am aware, there is no way to change a property that isn’t at the beginning of the property chain, because the change will create a new property there.
Are there plans to change this? It would be nice to have something akin to class methods (without accessing the prototype via some other, e.g. global, reference).
In order to access a reference to the prototype of an object, you can use Object.getPrototypeOf.
Right. That's what I didn’t think of: Iterating over the prototype chain until you get to the object that actually owns a given property and then set it there.
Thanks,
Axel
On Sun, Apr 10, 2011 at 6:59 PM, Axel Rauschmayer <axel at rauschma.de> wrote:
Exactly what I was looking for. Thanks.
Not exactly...
On Apr 10, 2011, at 22:18 , David Herman wrote:
function getDefiningObject(obj, key) { if (!(key in obj)) throw new Error("key " + key + " not found"); while (!obj.hasOwnProperty(key))
That should be
while (!{}.hasOwnProperty.call(obj, key))
which works even if obj has an own property named 'hasOwnProperty'.
I wondered if someone was going to make this point.
That should be
while (!{}.hasOwnProperty.call(obj, key))
which works even if obj has an own property named 'hasOwnProperty'.
Not if someone mutates Object.prototype.hasOwnProperty or Function.prototype.call. I don't think we need to polish a proof of concept.
Exactly what I was looking for. Thanks.
Not exactly...
True. But it's OK for me as a work-around.
That should be
while (!{}.hasOwnProperty.call(obj, key))
which works even if obj has an own property named 'hasOwnProperty'.
IIRC, {} creates a new instance for each invocation. Thus, a better solution would be while (!Object.prototype.hasOwnProperty.call(obj, key))
But I usually prefer David’s more readable version and take the risk of a name clash.
On 11/04/2011, at 05:14, Mark S. Miller wrote:
On Apr 10, 2011, at 22:18 , David Herman wrote:
function getDefiningObject(obj, key) { if (!(key in obj)) throw new Error("key " + key + " not found"); while (!obj.hasOwnProperty(key))
That should be
while (!{}.hasOwnProperty.call(obj, key))
which works even if obj has an own property named 'hasOwnProperty'.
And also because that would work for null too, unlike (null).hasOwnProperty( key ) that would throw "TypeError: 'null' is not an object (evaluating '(null).hasOwnProperty')", and null is at the end of the prototype chain of all objects...
Here's another take on it:
function getDefiningObject (o, key) { do { if ( {}.hasOwnProperty.call(o, key) ) break; } while ( o = Object.getPrototypeOf(o) ); return o; }
getDefiningObject([1,2], 'length') [1, 2] getDefiningObject([1,2], 'pop') [] getDefiningObject([1,2], 'valueOf') Object getDefiningObject([1,2], 'none') null
Personally, I prefer Object.getOwnPropertyDescriptor as a property existence test because it doesn't have the reflection the meta-circularity concern. I would write Dave's original function as:
function getDefiningObject(obj, key) { var obj={}.valueOf.call(obj); // ToObject in case primitive value passed while (obj) { if (Object.getOwnPropertyDescriptor(obj,key)) return obj; obj = Object.getPrototypeOf(obj); } throw new Error("key " + key + " not found"); }
On Apr 11, 2011, at 6:36 PM, Allen Wirfs-Brock wrote:
Personally, I prefer Object.getOwnPropertyDescriptor as a property existence test because it doesn't have the reflection the meta-circularity concern. I would write Dave's original function as:
function getDefiningObject(obj, key) { var obj={}.valueOf.call(obj); // ToObject in case primitive value passed while (obj) { if (Object.getOwnPropertyDescriptor(obj,key)) return obj; obj = Object.getPrototypeOf(obj); } throw new Error("key " + key + " not found"); }
Then the only downside is the pd allocation. Any way to avoid that?
Le 11/04/2011 21:47, Brendan Eich a écrit :
On Apr 11, 2011, at 6:36 PM, Allen Wirfs-Brock wrote:
Personally, I prefer Object.getOwnPropertyDescriptor as a property existence test because it doesn't have the reflection the meta-circularity concern. I would write Dave's original function as:
function getDefiningObject(obj, key) { var obj={}.valueOf.call(obj); // ToObject in case primitive value passed while (obj) { if (Object.getOwnPropertyDescriptor(obj,key)) return obj; obj = Object.getPrototypeOf(obj); } throw new Error("key " + key + " not found"); } Then the only downside is the pd allocation. Any way to avoid that?
Could static analysis help out to detect that the object is not going to be used and consequently avoid the allocation?
On Apr 11, 2011, at 8:58 PM, David Bruant wrote:
Le 11/04/2011 21:47, Brendan Eich a écrit :
On Apr 11, 2011, at 6:36 PM, Allen Wirfs-Brock wrote:
Personally, I prefer Object.getOwnPropertyDescriptor as a property existence test because it doesn't have the reflection the meta-circularity concern. I would write Dave's original function as:
function getDefiningObject(obj, key) { var obj={}.valueOf.call(obj); // ToObject in case primitive value passed while (obj) { if (Object.getOwnPropertyDescriptor(obj,key)) return obj; obj = Object.getPrototypeOf(obj); } throw new Error("key " + key + " not found"); } Then the only downside is the pd allocation. Any way to avoid that? Could static analysis help out to detect that the object is not going to be used and consequently avoid the allocation?
Of course, but that's a bit much and unless it is adopted by all the "bigs", web developers won't count on it and may shy away from the allocation (based on real measurements or premature optimization, doesn't matter).
It's not the end of the world to have a pd allocation, of course. But an API that avoids it without analysis might be worth considering.
On Apr 11, 2011, at 12:47 PM, Brendan Eich wrote:
Then the only downside is the pd allocation. Any way to avoid that?
/be
Use a GC that supports cheap allocation/recovery of short-lived objects :-)
It's probably premature optimization to worry about that one pd allocation without knowing more about the actual use case. I suspect that in many cases, the caller would actually want to get the pd back and so it would probably be best to return: {object: obj, desc: pd}. Of course that has yet another ephemeral object allocation. But really, if you over worry about such allocations you end up doing FORTRAN-like coding.
Maybe it's time to work on some JavaScript ephemeral GC benchmarks...
Le 11/04/2011 22:01, Brendan Eich a écrit :
On Apr 11, 2011, at 8:58 PM, David Bruant wrote:
Le 11/04/2011 21:47, Brendan Eich a écrit :
On Apr 11, 2011, at 6:36 PM, Allen Wirfs-Brock wrote:
Personally, I prefer Object.getOwnPropertyDescriptor as a property existence test because it doesn't have the reflection the meta-circularity concern. I would write Dave's original function as:
function getDefiningObject(obj, key) { var obj={}.valueOf.call(obj); // ToObject in case primitive value passed while (obj) { if (Object.getOwnPropertyDescriptor(obj,key)) return obj; obj = Object.getPrototypeOf(obj); } throw new Error("key " + key + " not found"); } Then the only downside is the pd allocation. Any way to avoid that? Could static analysis help out to detect that the object is not going to be used and consequently avoid the allocation? Of course, but that's a bit much and unless it is adopted by all the "bigs", web developers won't count on it and may shy away from the allocation (based on real measurements or premature optimization, doesn't matter).
Actually I was wrong I think. With the introduction of proxies, ES engines won't be able to trivially prevent pd allocation as they could with regular objects just based on static analysis (since static analysis will often fail at saying whether the function will be used with regular objects or proxies as first argument)
It's not the end of the world to have a pd allocation, of course. But an API that avoids it without analysis might be worth considering.
I think it could be possible if we had a native Object.hasOwnProperty method since that's really what we need here (that's what a static analysis optimization would do internally).
On Apr 11, 2011, at 9:21 PM, David Bruant wrote:
Actually I was wrong I think. With the introduction of proxies, ES engines won't be able to trivially prevent pd allocation as they could with regular objects just based on static analysis (since static analysis will often fail at saying whether the function will be used with regular objects or proxies as first argument)
Static analysis is conservative, yes. But type inference often can say what exact types |this| and other arguments have.
Anyway, this isn't about absolute guarantees unless JS provides an API to query without allocating :-P.
I think it could be possible if we had a native Object.hasOwnProperty method since that's really what we need here (that's what a static analysis optimization would do internally).
That was left out on purposes during ES3.1/ES5 design to keep things minimal. And of course Proxies built on that API. So probably we should leave this as an optimization challenge (GC, type inference, both if possible).
var obj={}.valueOf.call(obj); // ToObject in case primitive value passed
Can you explain how this works?
-
Why {} and not Object.prototype?
-
I know valueOf as a method that returns a primitive if an object can be converted to one and |this|, otherwise. Oddly enough, this works both in strict mode and in non-strict mode, as explained in the comment.
Thanks!
Axel
On Apr 11, 2011, at 6:45 PM, Axel Rauschmayer wrote:
var obj={}.valueOf.call(obj); // ToObject in case primitive value passed
Can you explain how this works?
Why {} and not Object.prototype?
I know valueOf as a method that returns a primitive if an object can be converted to one and |this|, otherwise. Oddly enough, this works both in strict mode and in non-strict mode, as explained in the comment.
{}.valueOf is the same value as Object.prototype.valueOf assuming none of the standard definitions have been over-written. See the comment thread of www.wirfs-brock.com/allen/posts/166 for a debate as to which of these forms is more or less optimizable.
Object.prototype.valueOf as specified is essentially a call to the internal ToObject operation that wrappers primitive values. I put it in to deal with cases like getDefiningObject("some string", "match")
On Tue, 12 Apr 2011 07:55:26 +0200, Allen Wirfs-Brock
<allen at wirfs-brock.com> wrote:
On Apr 11, 2011, at 6:45 PM, Axel Rauschmayer wrote:
var obj={}.valueOf.call(obj); // ToObject in case primitive value
passed ... {}.valueOf is the same value as Object.prototype.valueOf assuming none
of the standard definitions have been over-written. See the comment
thread of www.wirfs-brock.com/allen/posts/166 for a debate as to
which of these forms is more or less optimizable.Object.prototype.valueOf as specified is essentially a call to the
internal ToObject operation that wrappers primitive values. I put it in
to deal with cases like getDefiningObject("some string", "match")
In that case, why not just obj = Object(obj);
It has the added advantage of not returning the global object for null and
undefined,
but just a plain new object (although either can be said to be wrong in
this case).
Object.prototype.valueOf as specified is essentially a call to the internal ToObject operation that wrappers primitive values. I put it in to deal with cases like getDefiningObject("some string", "match")
Oh my, I've just consulted the ECMAScript spec and it's true. But isn't that counter-intuitive?
- Object.prototype.valueOf: convert to object
- String.prototype.valueOf: convert to primitive (for instances of String and primitive strings)
- Number.prototype.valueOf: same as above, but for numbers
- etc.
Even if that can't be fixed any more, there could be a method Object.toObject() with more obvious semantics. I try to avoid "expert magic" when using a programming language and it seems like a shame that this kind of magic is necessary here ( {} and valueOf()).
Greetings,
Axel
On Tue, Apr 12, 2011 at 3:57 AM, Lasse Reichstein < reichsteinatwork at gmail.com> wrote:
On Tue, 12 Apr 2011 07:55:26 +0200, Allen Wirfs-Brock < allen at wirfs-brock.com> wrote:
On Apr 11, 2011, at 6:45 PM, Axel Rauschmayer wrote:
var obj={}.valueOf.call(obj); // ToObject in case primitive value passed
...
{}.valueOf is the same value as Object.prototype.valueOf assuming none of
the standard definitions have been over-written. See the comment thread of www.wirfs-brock.com/allen/posts/166 for a debate as to which of these forms is more or less optimizable.
Object.prototype.valueOf as specified is essentially a call to the internal ToObject operation that wrappers primitive values. I put it in to deal with cases like getDefiningObject("some string", "match")
In that case, why not just obj = Object(obj);
It has the added advantage of not returning the global object for null and undefined,
For null and undefined, Object.prototype.valueOf throws. It does not return the global object.
On Apr 12, 2011, at 12:57 AM, Lasse Reichstein wrote:
In that case, why not just obj = Object(obj);
It has the added advantage of not returning the global object for null and undefined, but just a plain new object (although either can be said to be wrong in this case).
I used {}.valueOf in order to avoid converting null and undefined to a new object which would then be used as the starting point of the lookup.
In ES5 {}.valueOf.call(undefined) doesn't yield the this object. In general, ES5 built-ins do not convert their this parameter to the global object when it is undefined.
On Tue, Apr 12, 2011 at 08:41, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
In ES5 {}.valueOf.call(undefined) doesn't yield the this object. In general, ES5 built-ins do not convert their this parameter to the global object when it is undefined.
FYI
code.google.com/p/v8/issues/detail?id=1321, bugs.webkit.org/show_bug.cgi?id=58338
As far as I am aware, there is no way to change a property that isn’t at the beginning of the property chain, because the change will create a new property there.
Are there plans to change this? It would be nice to have something akin to class methods (without accessing the prototype via some other, e.g. global, reference).