tags:

views:

59

answers:

1

Why can't I access the 'x' property from A?

A = {}; B = {y: 'y'}; C = {x: 'x'}; A.prototype = B; B.prototype = C; A.x
+4  A: 

The prototype of an object is not reflected by the prototype property (confusing though that may be). In fact, there is no standard, direct way of setting the prototype of an object after you've created it (and it's only very recently that there's been any standard way to retrieve an object's prototype). In the code you've quoted, all you've done is create a normal property called prototype on the objects; it may as well have been called foo. :-)

There is a special prototype property in JavaScript, but it's on functions: It's the object that will be assigned as the prototype of the objects created by that function if you use it with the new keyword (e.g., as a constructor function). In fact, until very recently the only way to assign a prototype to an object was through a constructor function:

function makeA() {
}
makeA.prototype.x = "foo";

A = new makeA();
alert(A.x); // alerts "foo"

...and that's still the only widely-supported means of doing it.

Things are changing slightly with the new ECMAScript 5th edition specification, but you can still only set the prototype of an object when you create it, not after the fact. What the new stuff does is make it possible to do it directly rather than by making a constructor function to do it. This is the new Object.create feature in Section 15.2.3.5, but it's not widely supported yet. (There are several other nifty features of the 5th edition, including the ability to get the prototype of an object via Object.getPrototypeOf; but still no way to change it after the fact.)

There is a non-standard property that some implementations (like Firefox's SpiderMonkey engine) provide, __proto__, but it's not supported broadly and was not included in the latest specification.

Using the new Object.create, you could do what you want like this:

var C = {x: 'x'};
var B = Object.create(C);
B.y = 'y';
var A = Object.create(B);
alert(A.x); // alerts "x"

...but since Object.create is so new, you'll find that in most implementations, you need to create it because it's not there. That's easy enough if we ignore its second argument (which can only be partially emulated):

if (!Object.create) {
    // An *incomplete* emulation of Object.create, doesn't support the optional
    // second parameter defined by the spec.
    Object.create = function(proto) {
        var obj;

        // There's no point in calling this with a null or otherwise "falsy"
        // prototype, but let's handle it if you do
        if (!proto) {
            return {}; // Just a generic object
        }

        // Define a constructor object that uses
        // the prototype
        function ctor() {
        }
        ctor.prototype = proto;

        // Create and return the object
        return new ctor();
    };
}
T.J. Crowder
Your `Object.create` example gives me a `TypeError`. According to [MDC](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/create) you should have something like this: `var B = Object.create(C, { y: {value:'y'} });`
patrick dw
@patrick, @TJ, yes, the `properties` argument should be an object that contains property descriptors, if `Object.defineProperty` or `Object.defineProperties` are not available is better to not support this argument, because you can't really mimic the property attributes in ES3. This `Object.create` shim has also another problem, the standard `Object.create` method, can accept `null` to create an object that doesn't inherit from anything, in this case, the `new` operator will replace `null` for `Object.prototype`, I also would recommend throwing in that case... [See also...](http://j.mp/al68q0)
CMS
@patrick, @CMS: Thanks guys, that's what I get for dashing something off and disappearing. Much appreciated.
T.J. Crowder
Thanks! This is exactly what I was looking for.
Vanson Samuel