Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Traversing prototype chain using constructor.prototype

If I can use obj.constructor.prototype to access the prototype of an object, then why can't I use obj.constructor.prototype.constructor.prototype to traverse the prototype chain and have to use Object.getPrototypeOf?

function MyConstructor()
{
    this.prop = 1;
}

var o = new MyConstructor();

console.log(o.constructor.prototype) // MyConstructor

console.log(o.constructor.prototype.constructor.prototype) // MyConstructor?

Shouldn't it return the prototype of MyConstructor which is function() { [native code] } (in Chrome console)?

like image 758
user1019031 Avatar asked Jun 30 '13 20:06

user1019031


2 Answers

All constructors are instances of the global Function object:

function Foo(){ this.x = 1 }; // Dummy constructor function

console.log(Foo instanceof Function) // => true; Foo is an instance of global Function constructor

All prototypes are instances of the global Object object:

console.log(Foo.prototype instanceof Object); // => true

When a constructor Foo is defined, it automatically has a Foo.prototype object attached to it, which you can think of as a "blank" object that itself, as per the above, inherits from the global Object object. In other words, the prototype of Foo.prototype is Object.prototype:

function Foo(){ this.x = 1 }; // Dummy constructor function

console.log(Foo.prototype); // Foo (object); already exists

console.log(Object.getPrototypeOf(Foo.prototype) === Object.prototype); // => true

Since Foo.prototype is a blank object, one would expect its constructor to be the global Object constructor function:

function Foo(){ this.x = 1 }; // Dummy constructor function

console.log(Foo.prototype.constructor) // => function Foo() { this.x = 1; } ??

console.log(Foo.prototype.constructor === Object.prototype.constructor); // => false

However this "blank" object has an explicit self-referential constructor property that points back to function Foo(){ this.x = 1 } and overwrites or "masks" the default constructor property you are expecting:

function Foo(){ this.x = 1 }; // Dummy constructor function

delete Foo.prototype.constructor; // Delete explicit constructor property

console.log(Foo.prototype.constructor) // => function Object() { [native code] }

console.log(Foo.prototype.constructor === Object.prototype.constructor); // => true

Therefore you can't use obj.constructor.prototype recursively to traverse the prototype chain and have to rely on the Object.getPrototypeOf() method.

Here's a great visual reference.

like image 159
dkugappi Avatar answered Sep 20 '22 03:09

dkugappi


If I can use obj.constructor.prototype to access the prototype of an object

You can't in general. Consider how this approach works:

var proto = MyConstructor.prototype;
// has an (nonenumberable) property "constructor"
proto.hasOwnProperty("constructor"); // `true`
// that points [back] to
proto.constructor; // `function MyConstructor() {…}`

As you see, that's a circular property structure. When you do

var o = new MyConstructor();
// and access
o.constructor; // `function MyConstructor() {…}`
// then it yields the value that is inherited from `proto`
// as `o` doesn't have that property itself:
o.hasOwnProperty("constructor"); // `false`

But that only works for object like o that inherit the constructor property from their prototype object and where that has a useful value with something pointing to the prototype object. Think of

var o = {};
o.constructor = {prototype: o};

Oops. Accessing o.constructor.prototype yields o itself here, and it could have been any other nonsensical value. The structure actually is the same as above with MyConstructor.prototype - and if you access proto.constructor.prototype.constructor.prototype[.constructor.prototype…] you won't get anything else than just proto.

then why can't I use obj.constructor.prototype.constructor.prototype to traverse the prototype chain and have to use Object.getPrototypeOf?

Because you're trapped in the circular structure as MyConstructor.prototype) has that constructor property itself and not inherited from Object.prototype. For really getting the next object the true prototype chain, you have to use Object.getPrototypeOf.

var o = new MyConstructor();
console.log(o.constructor.prototype) // MyConstructor

It should have been MyConstructor.prototype actually. Chrome console sometimes gets confused at displaying useful titles for unnamed objects though, and is not always correct.

If you get its prototype, it should yield Object.prototype, and when you get the prototype of the MyConstructor function itself it should be Function.prototype. Notice that you can do the latter by MyConstructor.constructor.prototype again…

like image 30
Bergi Avatar answered Sep 21 '22 03:09

Bergi