Destroying constructor.prototype.constructor

Two days ago we replicated the new operator in JavaScript. In doing so, I noticed a quirky little trend with inheritance, and decided to look into it. In this post, we'll do just that, and then we'll also replicate the instanceof operator, since it can be equally confusing as new. Alright, let's go.

To start, check out the following code:

function Car( color ){
  this.color = color;
}

var car = new Car( "blue" );

car.__proto__ === car.constructor.prototype; // true
car.constructor; // function Car( color ) { this.color = color; }

function Truck( color ){
  this.color = color;
}

// manually set the prototype property
Truck.prototype = { fourWheelDrive: true };

var truck = new Truck( "blue" );

truck.__proto__ === truck.constructor.prototype; // false
truck.constructor; // function Object() { [native code] }

Very interesting. Why is this? Well, it turns out that when you create a function, JavaScript sets a default prototype property. This default property is an object with two properties itself: constructor and __proto__. When you create an object using an object literal {...}, __proto__ gets defined, but constructor does not. Therefore, when we override Truck.prototype with an object literal, we lose the property Truck.prototype.constructor. Now, when we instantiate some trucks, they have to go all the way up the prototype chain to Object.prototype.constructor to find a constructor property. Sure enough, we can check, and we see that Object.prototype.constructor === truck.constructor; // true

Looking at the two constructor functions' prototypes gave a good clue that something like this was happening

Car.prototype; // Car
Truck.prototype; // Object

As a quick aside, the original prototype.constructor property that gets initialized on a function is actually a circular reference to the function itself. Check this out:

function hey(){}
hey.prototype.constructor.prototype.constructor.prototype.constructor === hey // true!

Great! So that makes some sense, but why does it matter? Well, if your relying on the constructor property of an object anywhere to accurately reflect the prototype chain, then manually setting a constructor function's prototype with an object literal is a bad idea. Here are a couple good solutions:

// 1. set the constructor property manually.
Truck.prototype = {
  fourWheelDrive: true,
  constructor: Truck
};

// 2. set properties on the prototype individually,
// so as not to overwrite the existing prototype object:
Truck.prototype.fourWheelDrive = true;

// 3. don't really care about the constructor property
// and try to make sure you aren't relying on it to behave consistently.

// Note:
// I think I'm going to go with option 2 in my code, 
// but option 1 is also super useful for demo purposes,
// and option 3 is fine in > 95% of cases.

Okay, sounds good, but there's still one curious bit:

truck instanceof Truck; // true

The reason that this is curious is because we destroyed the link from constructor.prototype to Truck.prototype. However, we did not destroy the link via the __proto__ property.

Looking closely at some examples on the Mozilla Developer Network, we can learn that the instanceof operator, when called like: left_arg instanceof right_arg looks up the prototype chain of the left_arg for right_arg.prototype. Let's implement this real fast:

function instanceOf(object, constructor){
  // we start one level up the prototype chain
  var tempObject = object.__proto__;
  while(tempObject !== null){
    if(tempObject === constructor.prototype){
      return true;
    }
    else{
      tempObject = tempObject.__proto__;
    }
  }
  return false;
}

It would also be not too hard to do this recursively:

function recursiveInstanceOf(object, constructor){
  // ignore the case when object === constructor.prototype the first time
  var tempObject = object.__proto__;  
  // base case
  if( object === null ){
    return false;
  } else if ( object === constructor.prototype ) {
    return true;
  } else {
    // look up the prototype chain
    return recursiveInstanceOf( object.__proto__, constructor );
  }
}