Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difficulty Manually Walking The Prototype Chain

I wanted to try manually walking the prototype chain of a few objects just to see what I find along the way. However, I got stuck on the first one that I tried. Here's the code:

function MyObject() { }
var x = new MyObject();
console.log('--------------------------------------------');
console.log('x.constructor.name: ' + x.constructor.name);
console.log('x.constructor.prototype.constructor.name: ' + x.constructor.prototype.constructor.name);
console.log(x.constructor.prototype === Function.prototype ? 'Good guess.' : 'No, you are wrong.');
console.log(x.constructor === MyObject ? 'Good guess.' : 'No, you are wrong.');
console.log('--------------------------------------------');

The above code results in the following output in the Developer Tools Console of Google Chrome:

--------------------------------------------
x.constructor.name: MyObject
x.constructor.prototype.constructor.name: MyObject
No, you are wrong.
Good guess.
--------------------------------------------

It makes sense that x's constructor is the MyObject function, since x was instantiated using the new keyword on MyObject (this follows from the definition of a constructor). Because of this, I understand the first line of output (note: I started counting lines of output from zero on up). The second line, however, confuses me. I would like to know what MyObject's prototype is. Apparently, it isn't an object of type Function, as indicated by the 3rd line of output, which tells me that I'm wrong. The fourth line of output just reinforces the output from the first line.

On a more general note, I assumed that the correct way to walk the prototype chain of an object would be to go from the object in question to its constructor, and then from the constructor to the constructor's prototype, assuming that this last reference is not null. I assumed that this two-step process (consisting of going to the constructor, and then to the constructor's prototype) should be repeated until a null reference from a constructor to a prototype is reached, thus signifying the end of the prototype chain. This doesn't seem to be working, though, since application of this algorithm to the above scenario just leads to circular references.

In summary, I have two questions:

  1. Why is x.constructor === x.constructor.prototype.constructor (or, in other words, why the circular references), and what kind of an object is x.constructor.prototype, anyway (or, in other words, how did it get instantiated / where did it come from)?
  2. How can the above algorithm be corrected in order to correctly walk the prototype chain for object x?

Edit

I came across a similar question on StackOverflow here, but it doesn't explicity ask the correct way to walk the prototype chain. It does point out the circular references, though...

like image 728
Andrew Avatar asked Apr 30 '15 19:04

Andrew


1 Answers

In Javascript terminology, an object a's "prototype" refers to the object from which a inherits properties. The standards-based way to access this is with Object.getPrototypeOf:

var protoOfA = Object.getPrototypeOf(a);

There's also the old way, non-standard but supported by some browsers:

var protoOfA = a.__proto__;

But if you have a function F, F.prototype does NOT refer the object from which F inherits anything. Rather, it refers to the object from which instances created by F inherit:

function F() {};
a = new F();
console.log(Object.getPrototypeOf(a) === F.prototype); // true

When you define a function, an object is created to serve as the prototype of instances created by that function, and this new object is stored in the function's prototype property.

--

Functions behave like objects in many ways (e.g., they can have properties) but they aren't exactly like other objects:

console.log(typeof a); // "object"
console.log(typeof F); // "function"

Their "prototypes" are ill-defined (example run in Chrome) (this is apparently a Chrome-specific behavior)

console.log(Object.getPrototypeOf(F)); // "function Empty() {}"
console.log(Empty);                    // ReferenceError: Empty is not defined

--

The constructor property is strange. The interpreter doesn't care about it. MDN says, confusingly:

Returns a reference to the Object function that created the instance's prototype.

Further, you can change the value of constructor on an object, but this has no effect on what the object is or how it behaves - it's merely descriptive.

--

So, to answer your questions:

Why is x.constructor === x.constructor.prototype.constructor

No good reason. This is arbitrary behavior browsers have converged on.

what kind of an object is x.constructor.prototype, anyway

In this example, t's x's prototype, the same as Object.getPrototypeOf(x). But in general you can't rely on x.constructor or anything derived from it, because it's arbitrary.

How can the above algorithm be corrected in order to correctly walk the prototype chain for object x?

for (var p = x ; p != null ; p = Object.getPrototypeOf(p)) {
  // do something with p
}
like image 130
radiaph Avatar answered Oct 27 '22 02:10

radiaph