Historically one JavaScript property that’s seen a lot of use (mostly due to its convenience) is that of __proto__
. It’s a quick-and-dirty way of accessing the original prototype property of the object’s constructor function. For example, the following is true:
"test".__proto__ === String.prototype // Another alternative, not using __proto__ // Only works when constructor isn't changed "test".constructor.prototype === String.prototype
This feature has been codified in the upcoming ECMAScript 3.1 specification as a new method: Object.getPrototypeOf(object)
(and is implemented in the latest Firefox nightlies).
So how can we make use of this, now standardized, functionality?
instanceOf
If you wish you could implement your own version of the instanceof
operator, using pure JavaScript.
function instanceOf(object, constructor) { while (object != null) { if (object == constructor.prototype) return true; object = Object.getPrototypeOf(object); } return false; } instanceOf("test", String); instanceOf(true, Boolean);
In this example we traverse up the prototype chain, checking each constructor along the way. It’s an effective method and allows for great expressiveness in our code.
Super Methods
We could use this function to call a super method, while doing some inheritance.
function Person(){} Person.prototype.kick = function(type){ alert(type + " kick!"); } function Norris(){} // Inherit properties from Person Norris.prototype = new Person(); Norris.prototype.kick = function(){ Object.getPrototypeOf(this).kick("Roundhouse"); };
In the above code we use Object.getPrototypeOf(this)
to tap in to the original, inherited, kick
method. Since we have since overridden the method we don’t have direct access to it, but using getPrototypeOf
we can capture, and utilize, it again.
Cross-Browser Implementation
The obvious question now becomes: How do we begin using Object.getPrototypeOf
today if most browsers don’t have it implemented yet? In the meantime we can use something like the following code for some form of compatibility:
if ( typeof Object.getPrototypeOf !== "function" ) { if ( typeof "test".__proto__ === "object" ) { Object.getPrototypeOf = function(object){ return object.__proto__; }; } else { Object.getPrototypeOf = function(object){ // May break if the constructor has been tampered with return object.constructor.prototype; }; } }
While it’s not 100% spot-on (since the .constructor
property is mutable on any object – it’s fully possible that it could’ve been manipulated by the user at some point) the above code should serve as a “good enough” solution to tide you over until browsers have good ECMAScript 3.1 compatibility.
Why Object.getPrototypeOf?
A common question at this point (and one that’s sure to come up often as new features start to trickle in from ECMAScript 3.1): Why is the method Object.getPrototypeOf("test")
and not "test".getPrototypeOf()
– or even just a property, like __proto__
? While having a method, or property, on every object would certainly be more convenient to use it ends up being impractical for generalized use.
For example, take the following case into consideration:
var obj = { getPrototypeOf: "blah" };
Any attempt to call its getPrototypeOf
method would end in failure, forcing the developer to always have to fall back to using the generalized Object.getPrototypeOf
. Since most uses of getPrototypeOf
would be required to work in the general case the fallback would always have to be used. Thus it’s not necessary to include it as an extra property of every object.
Additionally, it makes it far easier to backport to old implementations since you no longer have to extend the Object prototype (Object.prototype.getPrototypeOf = ...;
) which would’ve been bad in any case.
Malte (August 14, 2008 at 11:40 pm)
Hey,
ist there also a setPrototypeOf? (Because we can assign to __proto__ now to “rebless” (in Perl terms) an object)
Bye
malte
Jake Archibald (August 15, 2008 at 1:46 am)
Hmm, I don’t really buy the reasons for not making it an instance method / property. Yeah there’s a possibility it could be overwritten, but isn’t that true of all properties in JavaScript?
Even with the suggested solution I could do:
Object.getPrototypeOf = "socks";
…and
Object.getProtoTypeOf()
would fail.var myObj = {"constructor":"guff"};
…would break stuff that expected the standard implementation of
Object#constructor
. Isn’t this just something we have to live with in javascript for the convenience of instance properties?If we’re making
getPrototypeOf
a static method, shouldn’t the same be done withhasOwnProperty
? Surely they should both be static methods, or both instance methods rather than a mix.Jake.
Jeria (August 15, 2008 at 5:29 am)
Man, I wish I could just type
Norris.prototype.kick = function(){
super.kick("Roundhouse");
};
or even better (inside Norris class)
public kick():string {
super.kick("Roundhouse");
}
Simple and intuitive.
John Resig (August 15, 2008 at 7:07 am)
@Malte: There isn’t a setPrototypeOf, as far as I know. Naturally you could modify the returned object though (but I agree that it isn’t as elegant as simply modifying __proto__ outright).
@Jake: Dunno – you’re welcome to take it up with the working group, that was their rationale concerning the subject. I’m not sure if it’s the best solution, but that’s what they arrived at.
@Jeria: Well, there was functionality like that in ECMAScript 4 – but as I wrote before (when talking about ECMAScript Harmony) that’s quite up in the air at this point.
Nicolas (August 15, 2008 at 7:09 am)
Why “public”?. I mean, why another way of doing something you already can do avoiding the “public” keyword? (and being also concise).
I’d just love to replace the “function” keyword with some shorter keyword, like “fn”.
Jeria (August 15, 2008 at 7:17 am)
@Resig: Yeah I read that, pity, since it’s code like the one above (and I do not specifically mean your code, its great, I mean that you actually need to write code like the one above to just achieve what you would with the super keyword) that scares developers away from JavaScript.
Noah (August 15, 2008 at 7:21 am)
This seems wrong to me:
Norris.prototype.kick = function(){
Object.getPrototype(this).kick("Roundhouse");
};
Object.getPrototype(this)
should be returning Norris.prototype, which should have the above kick function, not the kick function from Person. Otherwise,Object.getPrototype(new Norris()) !== Norris.prototype
.Noah (August 15, 2008 at 7:32 am)
Seems like it should be
Object.getPrototype(Object.getPrototype(this)).kick("Roundhouse");
. And you could implement a generic super function as something like:function super(obj,method) {
obj = Object.getPrototype(obj)
if(!obj) return;
do {
obj = Object.getPrototype(obj);
} while(obj && !obj[method]);
return obj && obj[method];
}
// Usage
super(this,"kick").call(this,"Roundhouse");
Noah (August 15, 2008 at 8:30 am)
Ah, I see it now.
Object.getPrototype(this)
returnsNorris.prototype
.Norris.prototype.kick
calls the same function, butObject.getPrototype(this)
returnsPerson.prototype
the 2nd time, since it is being called onNorris.prototype
, so it does eventually get to the right ‘kick’.John Resig (August 15, 2008 at 8:45 am)
@Nicolas: What are you referring to? Jeria was just pointing out some hypothetical code – JavaScript/ECMAScript isn’t moving in that direction.
@Jeria: I can agree with that. We’ll have to see if any syntactic sugar comes along to make these things easier.
@Noah: You’re welcome to test it – it works in the Firefox nightly – here’s my test page:
http://ejohn.org/files/proto.html
In thinking about it more, though, it’s definitely not logical – and perhaps may even be wrong. This is the series of calls that occur:
[instance].kick()
=> Object.getPrototypeOf([instance]) (returns Norris.prototype)
=> Norris.prototype.kick("Roundhouse")
Norris.prototype.kick("Roundhouse")
=> Object.getPrototypeOf(Norris.prototype) (returns Person.prototype)
=> Person.prototype.kick("Roundhouse")
So yeah – not a proper super() method by any stretch – your solution might work well in this case.
Jeria (August 15, 2008 at 8:52 am)
@Noah: yeah, you could do it like that, but then again, my last post above would apply to your solution as well.
Ryan McCue (August 15, 2008 at 8:55 am)
FYI:
“This feature has been codified in the upcoming ECMAScript 3.1 specification as a new method: Object.getPrototpeOf(object)”
Should be getPrototypeOf.
John Resig (August 15, 2008 at 9:12 am)
@Ryan: Thanks – it looks like I mis-spelled this one a bunch. getPrototype(), getPrototpeOf(), heh.
Malte (August 15, 2008 at 9:55 am)
@John: Would modifying the return value of getPrototypeOf be local to the object? I would expect it to modify all objects that have the same prototype while assigning a new __proto__ property effectively enables assigning new behavior to an object in one step. I particularly need this behavior to be able to unapply traits from objects in my js meta system as demonstrated in the unapply method here: http://code.google.com/p/joose-js/source/browse/trunk/lib/Joose/Role.js
I haven’t found a way to do this in IE with its current JS implementation.
John Resig (August 15, 2008 at 10:32 am)
@Malte: You’re correct in assuming that the result of getPrototypeOf will point back to the original prototype object – not the one existing on this particular object (as is the case with __proto__). In that respect it isn’t as useful as __proto__.
aleto (August 15, 2008 at 4:43 pm)
Interesting, though i see problems with the Cross-Browser Implementation, constructor points to the same as constructor.prototype.constructor, at least for real js objects. ( e.g. with your instanceOf implementation you would end up in an endless loop in a browser without __proto__ support )
Garrett (August 18, 2008 at 12:51 am)
@John –
“In this example we traverse up the prototype chain, checking each
constructor
along the way.”That’s not quite accurate. The code doesn’t check *each*
constructor
, it gets eachprototype
and compares it to theprototype
property of theconstructor
parameter.The usefulness of
getPrototypeOf
is that it is reliable, unlike theconstructor
property:function F(){}
function G(){}
G.prototype = new F;
new G().constructor === F; // true, constructor property is dynamic.
There’s a little more going on in the
Norris
->Person
example. You’re recursively callingkick
and changing thethisArg to have static context. In effect, the end result is:
Person.prototype.kick.call(Norris.prototype, "Roundhouse")
…but it looks as if Noah pointed that out.
I already wrote up my comments, so might as well post it.
Garrett