3

Below are some examples showcasing the different behaviors of prototypal inheritance based on how the objects are defined and created. I distinguish between the "prototype property" of an object, e.g. someObject.prototype and the "prototype reference" (which I think should refer to the object from which someObject inherits?).

Example 1

This seems to be the way to preserve the parent object's prototype property. Isn't this the recommended way of inheriting?

// create object whose prototype reference is Object; add stuff.
var Parent = Object.create(Object);
Parent.a = "1";
Parent.f = function() { return true; };

// add stuff to prototype property
Parent.prototype.b = 1;
Parent.prototype.g = function() { return false; };

// create an object whose prototype reference is Parent (??)
var Child = Object.create(Parent);

console.log(Parent.__proto__)  // [Function: Object]
console.log(Parent.prototype)  // { b: 1, g: [Function] }
console.log(Child.__proto__)   // { a: '1', f: [Function] }
console.log(Child.prototype)   // { b: 1, g: [Function] }
  1. I would have expected Child.__proto__ to be something naming Parent in the same way Parent.__proto__ names Object.

  2. We see that Child's prototype reference points to the properties of Parent rather than the properties of Parent.prototype. This is counterintuitive, to me at least, as I would have expected to see Parent.prototype's properties b and g instead.

Example 2

Mixed results.

// use a constructor instead.
var Parent = function () {
    this.a = "1";
    this.f = function() { return true; };
}

// again, add stuff to prototype property.
Parent.prototype.b = 1;
Parent.prototype.g = function() { return false; };

// create an object whose prototype reference is Parent (??)
var Child = new Parent();

// create differently
var Sibling = Object.create(Parent);

console.log(Parent.__proto__)   // [Function: Empty]
console.log(Parent.prototype)   // { b: 1, g: [Function] }

console.log(Child.__proto__)    // { b: 1, g: [Function] }
console.log(Child.prototype)    // undefined

console.log(Sibling.__proto__)  // [Function]
console.log(Sibling.prototype)  // { b: 1, g: [Function] }
  1. Here, Child's prototype references Parents.prototype's properties. This is what I expected above?

  2. On the other hand, Sibling's prototype reference is now a function, which is the prototype reference of Parent.

Example 3

This seems to be the way to preserve the parent object's prototype reference, but you lose its prototype property.

// create object constructor; add stuff.
var Parent = function () {
    this.a = "1";
    this.f = function() { return true; };
}

// add stuff to prototype property.
Parent.prototype.b = 1;
Parent.prototype.g = function() { return false; };

// create an object whose prototype reference is Parent (??)
var Child = function() {
    this.c = "2";
};

// supposed Ad-hoc prototype inheritance
Child.prototype = Object.create(Parent.prototype)

console.log(Parent.__proto__)  // [Function: Empty]
console.log(Parent.prototype)  // { b: 1, g: [Function] }
console.log(Child.__proto__)   // [Function: Empty]
console.log(Child.prototype)   // {}

Is the method shown in Example 1 preferred since you have access to both the parent's prototype property, its own properties as well as Object's properties/methods? I've read in other posts that some of these ways of inheriting should be equal...which is not true. Also, I've read other posts, e.g. here, that Example 3 is the way to go. Or perhaps I am just not sure what __proto__ stands for...

Thanks for any clarification you might have for the differences between these mix and match situations!

Community
  • 1
  • 1
jayflo
  • 1,105
  • 1
  • 12
  • 21

2 Answers2

2

I distinguish between the "prototype member" of an object, e.g. someObject.prototype and the "prototype reference"

Good terminology, and distinguishing them is the first step in understanding them and the difference between them.

(which I think should refer to the object from which someObject inherits?).

Exactly. In contrast to the .prototype member, you acess the protypte reference via Object.getPrototypeOf(obj) or the non-standard .__proto__ property.

Example 1

Isn't this the recommended way of inheriting?

It's one way to do inheritance, and the most pure form of prototypical inheritance. Notice that Parent and Child are plain objects, no constructor functions are involved.

// create object whose prototype reference is Object; add stuff.
var Parent = Object.create(Object);

That's the first mistake: You're inheriting from the Object function (which is based on the constructor pattern, see below). You should just do

var Parent = {};
// or
var Parent = Object.create(Object.prototype);
// add stuff to prototype member
Parent.prototype.b = 1;
Parent.prototype.g = function() { return false; };

Yet, the .prototype member is of no significance in this model of inheritance. It's just a normal property, in this case one inherited from Object. You're actually modifying the Object.prototype object here (big no-no)!

// create an object whose prototype reference is Parent (??)
var Child = Object.create(Parent);

Yes, that's what Object.create does.

[Example 1 / 1] I would have expected Child.proto to be something naming Parent in the same way Parent.proto names Object.

It names Object only because that is a named function. There's no way to know the variable name Parent from the object reference at runtime.

[Example 1 / 2] We see that Child's prototype reference points to the members of Parent rather than the members of Parent.prototype. This is counterintuitive, to me at least, as I would have expected to see Parent.prototype's members b and g instead.

Yes, you're directly inheriting from the Parent object. .prototype is insignificant as stated above. Child.prototype === Object.prototype (it's inherited twice).

Example 2 - use a constructor instead. Adding stuff to its prototype member.

// create an object whose prototype reference is Parent (??)
var Child = new Parent();

[Example 2 / 1] Here, Child's prototype references Parents.prototype's members. This is what I expected above?

No. The new operator creates a prototype reference to the value of the .prototype member of the supplied constructor function - Parent.prototype in this case.

// create differently
var Sibling = Object.create(Parent);

Again, here you're creating an object that has a prototype reference to a function. Not what you want - instead use

var sibling = Object.create(Parent.prototype);
Parent.call(sibling); // optionall apply the constructor on it, like `new` does

[Example 2 / 2] On the other hand, Sibling's prototype reference is now a function, which is the prototype reference of Parent.

To be more exact, it is the Parent function itself. That's why your Sibling does inherit a .prototype property.

Example 3 - This seems to be the way to preserve the parent object's prototype reference, but you lose its prototype member:

// supposed Ad-hoc prototype inheritance
Child.prototype = Object.create(Parent.prototype)

This is necessary to set up the inheritance (prototype reference) chain for the constructor way. It's the standard way to implement a "class" pattern in JavaScript.

console.log(Parent.__proto__)  // [Function: Empty]
console.log(Parent.prototype)  // { b: 1, g: [Function] }
console.log(Child.__proto__)   // [Function: Empty]
console.log(Child.prototype)   // {}

As you see here, both Parent and Child are functions - constructor functions to be exact. You would need to invoke them:

var childInstance = new Child();
console.log(childInstance) // {c: "2"}
console.log(childInstance.prototype) // undefined - it's not a constructor
console.log(childInstance.__proto__) // {} - the Child.prototype object
console.log(childInstance.__proto__.__proto__) // the Parent.prototype object
console.log(childInstance.__proto__.__proto__.__proto__) // the Object.prototype object

However, you forgot one step. childInstance does inherit b and g from Parent.prototype, but does not have the a and f proeprties created in the Parent constructor. For correct inheritance in the constructor model you also have to apply the Parent constructor on each instance, preferably inside the Child constructor:

function Child() {
    Parent.call(this);
    this.c = "2";
}
var childInstance = new Child();
console.log(childInstance) // {a: 1, f: [Function …], c: "2"}
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Great post. I think I have a much better understanding of what's going on now in terms of the objects and references...but I will keep reading! The links you posted are interesting and led to many others. – jayflo Jun 03 '14 at 19:46
  • Question: In Example 3, can you also set the `Child`'s prototype inside the constructor like so? `var Child = function () { this.prototype = Object.create(Parent.prototype); Parent.call(this); }` This way, everything is self contained? – jayflo Jun 03 '14 at 20:30
  • No, inside the constructor `this` is the instance, not the constructor, and you'd need to set the prototype reference not some `.prototype` member. [Setting the `.prototype` in the constructor is a bad idea](http://stackoverflow.com/q/21296559/1048572), and [creating the instance in the constructor](http://stackoverflow.com/q/24019863/1048572) is not how the `new`-pattern works. – Bergi Jun 03 '14 at 21:16
0

To understand what Object.create does, first you need to understand why it needs to do it.

The classic examples of prototyping in JavaScript makes the prototype be an instance of the inherited object:

function Parent(field) {
    this.field = field;
}

Parent.prototype.getField = function () { return this.field; }

function Child(field, field2) {
    Parent.call(this, field);
    this.field2 = field2;
}

Child.prototype = new Parent(null); // oops

Child.prototype.getField2 = function () { return this.field2; }

That "oops" line makes Child.prototype have a field, when we wanted it to only inherit the method getField. You can guess this is not hygienic (language-wise), and it can get messy with less trivial examples.

A cleaner approach would be to instantiate a non-initialized Parent:

function inherit(parent) {
    function temp() {
    }
    temp.prototype = parent.prototype;
    return new temp();
}

Child.prototype = inherit(Parent);

The technique is to make a temporary constructor with the same prototype, such that when an object is created, no constructor initializations were done. Effectively, only the prototype chain has been established.

acelent
  • 7,965
  • 21
  • 39
  • notice that this is just what the OP does in the line `Child.prototype = Object.create(Parent.prototype)` – Bergi Jun 03 '14 at 00:07
  • @Bergi, I know, and I think it's also the source of the OP's confusion or lack of knowledge. – acelent Jun 03 '14 at 00:21