1

This question is about how javascript prototypes work. In particular I do not understand why in the example below "Machine" appears to have a prototype of itself.

In the screen capture from Chrome Web Tools console the Machine __proto __ object seems to reference the same function as its parent. You will see a "__proto __: Machine" with a property "whoAmI" and a second property "__proto __: Machine". Drilling into this second "__proto __" reveals a constructor and the "__proto __" containing Object but no property "whoAmI".

What is the meaning of the double reference to "Machine" and why does only one of them contain the whoAmyI property?

function Car(){}

function Vehicle() {}

function Machine() {
    this.whoAmI = 'I am a machine';
}

Vehicle.prototype = new Machine();

Car.prototype = new Vehicle();

var myCar = new Car();

Chrome web tools screen capture:

Chrome web tools screen capture

William Edmondson
  • 3,619
  • 3
  • 32
  • 41
  • 1
    That's just console output, so don't read too much into it. A preferred way to set up inheritance is to do `Vehicle.prototype = Object.create(Machine.prototype);`. Do this and you'll get different labels in Chrome's console, even though it's basically the same (though the `Vehicle.prototype` object won't have a `whoAmI` property). –  Nov 02 '14 at 03:47
  • 1
    I think part of the issue is that chrome uses the `.constructor` property to figure out the labels. The way you're setting up inheritance, you're destroying the original `.constructor` properties. If you do `Vechicle.prototype.constructor = Vehicle; Car.prototype.constructor = Car;`, you should get a more understandable output. –  Nov 02 '14 at 03:50

2 Answers2

3

It's better to use Object.create instead of creating an instance of Parent to set prototype of Child for (at least) 2 reasons.

  1. When you define the Child type you may not want to create instances of Parent at this point due to the state of the application not being ready to create instances of Parent type at this point (like dependencies that need to be passed to the Parent constructor that are not available when defining Child).

  2. Parent has instance specific members (any this.something = in Parent constructor) and creating an instance of Parent puts these instance specific members on Child.prototype.

That is why you see Machine (an instance of type Machine) with a whoAmi member and a Machine as the __proto__ of this Machine instance.

The following code may produce output you'd expect and because the constructor is fixed after setting prototype part of inheritance it displays the right name:

function Car(){
  //re use parent constructor
  Vehicle.call(this);
  //extend whoAmi
  this.whoAmI = this.whoAmI + ' and a Car';
}

function Vehicle() {
  //re use parent constructor
  Machine.call(this);
  //extend whoAmi
  this.whoAmI = this.whoAmI + ' and a Vehicle';
}

function Machine() {
    this.whoAmI = 'I am a machine';
}

Vehicle.prototype = Object.create(Machine.prototype);
Vehicle.prototype.constructor = Vehicle;

Car.prototype = Object.create(Vehicle.prototype);
Car.prototype.constructor = Car;

var myCar = new Car();
console.dir(myCar);

Maybe the following answer can help understanding the role of prototype and constructor functions (or init) when creating instances of a certain type:

Community
  • 1
  • 1
HMR
  • 37,593
  • 24
  • 91
  • 160
  • 2
    You can set the constructor all in one go with a second argument. `Car.prototype = Object.create(Vehicle.prototype, {constructor: value: Car}});` – Mulan Nov 02 '14 at 05:08
  • 1
    @naomik correct but the second argument cannot be polyfilled in some browsers (ie8). https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FObject%2Fcreate#Browser_compatibility – HMR Nov 02 '14 at 15:13
  • 1
    I'd agree with @naomik. As long as you're going to set the `.constructor`, might as well do it with one call. The second argument can be polyfilled. It's the `enumerable`, `writable` and `configurable` descriptors that can't be emulated. –  Nov 02 '14 at 16:25
1

Shortish answer if I understand your question: It's not actually a self reference. When you define a function, the prototype property on it gets initialized to a new object with a constructor property that points to the function being defined. You are overriding this prototype object on your Vehicle and Car functions, but not the Machine function. For reasons I don't know, Chrome names the type of the default prototype property of a function the same as the name of the function. IE refers to it as [object (Machine)] and Firefox just refers to it just as Object.

So to recap: myCar has __proto__ set to Car.prototype, which is a Vehicle instance that has it's __proto__ set to Vehicle.prototype, which is a Machine instance (with a whoAmI property) that has it's __proto__ set to Machine.prototype, which is an object, with a constructor property, that Chrome (unfortunately? there must be a reason...) refers to with the same name as the function pointed to by the constructor property.

That is why there is that extra "Machine" object in there, but I can't tell you why Chrome calls it what it does.

peterjb
  • 819
  • 7
  • 6