1

When reading https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call for the section "Using call to chain constructors for an object":

function Product(name, price) {
  this.name = name;
  this.price = price;

  if (price < 0)
    throw RangeError('Cannot create product "' + name + '" with a negative price');
  return this;
}

function Food(name, price) {
  Product.call(this, name, price);
  this.category = 'food';
}
Food.prototype = Object.create(Product.prototype);

function Toy(name, price) {
  Product.call(this, name, price);
  this.category = 'toy';
}
Toy.prototype = Object.create(Product.prototype);

var cheese = new Food('feta', 5);
var fun = new Toy('robot', 40);

I have read that a object's prototype is actually a object itself that points to the constructor's properties memory locations.

In function Food(name, price) it inherits the constructor's Product's properties with Product.call(this). What is Food.prototype = Object.create(Product.prototype); doing? Is it adding another prototype to Food(if that is even possible to have 2 prototypes)? Or is it appending to Food's prototype with the same prototype values of Product(which it already inherited so doesn't make sense to me ether)?

1
  • 1
    Food.prototype = Object.create(Product.prototype) is like extends in other languages. Product.call(this) is like super. Commented Jan 11, 2014 at 21:37

5 Answers 5

4

The essence of the problem cam be summarized as "why would you need to set prototypes at all"? In other words, why this is not sufficient to call the base class constructor and not to set the prototype?

The trick is that you could possibly not set the child class function's prototype! Your trivial example would work fine without it. Comment out the line and you will find that the code works with and without this line:

Food.prototype = Object.create(Product.prototype);

This is because all your object properties are set in constructors and by chaining constructors (calling the base class constructor) you have all the properties set on child class instances.

Consider however this:

function Base() {
   this.BaseClassMethod = function() {}
}

vs

function Base() {
}

Base.prototype.BaseClassMethod = function() {}

As you can see, there are no fields but a method. Semantically, both snippets define a constructor function that creates instances having the sole BaseClassMethod method. However, in the first snippet there is a new extra function created for each created instance of the class, while in the second snippet the method definition is shared.

This very simple example shows that the same semantics can be achieved in different ways that have different profiles - in the above example is was at least the different memory footprint.

Yet another difference between these two is that in the latter, the function can be redefined somewhere in the future and the new version affects all previously created instances while in the first snippet what you can do is to change the definition in specific instances but not easily "in all previously created instances".

Whether or not this is your goal is another story. You can freely choose between these two ways of assigning methods to classes.

And there lies the answer to your question: without setting the prototype chain, methods defined in base class prototype (the latter snippet) would not be inherited if you only called the base class constructor from the child class constructor.

In other words, calling both the base class constructor and setting the prototype chain makes your inheritance independent on the way methods are defined in the base class.

Sign up to request clarification or add additional context in comments.

Comments

1

Let's let code speak for itself. Object.create basically does this:

Object.create = function (o) {
  //Object.create equals an anonymous function that accepts one parameter, 'o'.

    function F() {};
    //Create a new function called 'F' which is just an empty object.

    F.prototype = o;
    //the prototype of the 'F' function should point to the
    //parameter of the anonymous function.

    return new F();
    //create a new constructor function based off of the 'F' function.
  };

Hope this helps. Cheers

1 Comment

I don't think this polyfill does help to understand prototypical inheritance. Explaining it the other way round (new in terms of Object.create and call) is much easier :-)
1

Food.prototype = Object.create(Product.prototype) is like extends in other languages. Product.call(this) is like super. With a helper and following conventions it helps to see this relation:

Function.prototype.inherits = function(parent) {
  this.prototype = Object.create(parent.prototype); // inherit parent's prototype
  this.prototype.constructor = this; // point to the right constructor
};

// A "class"
var Product = (function(){
  // constructor
  function Product(name, price) {
    this.name = name;
    this.price = price;
  }
  return Product;
}());

var Food = (function(_super){
  Food.inherits(Product); // inherit Product's prototype methods
  function Food(name, price) {
    // call "super" to inherit instance properties
    _super.call(this, name, price);
    this.category = 'food';
  }
}(Product)); // the parent class AKA "super" 

It is not 100% equivalent, but this should give you a general idea of how inheritance works in JS compared to other languages. It can look pretty similar as you see.

Comments

1

In function Food(name, price) it inherits the constructor's Product's properties with Product.call(this).

Not really. It applies the Product's constructor to the new Food instance, executing its code with e.g. the negative price check.

A "byproduct" is that the constructor creates instance-specific properties on the object, yes.

What is Food.prototype = Object.create(Product.prototype); doing? Is it adding another prototype to Food(if that is even possible to have 2 prototypes)?

Exactly. It is chaining the prototypes. The instances of Food that are created by new Food will inherit properties (including methods) from Food.prototype, which will (by that statement) inherit properties from Product.prototype.

You can't see much of this behaviour currently as your prototype objects do not have any methods yet. Add some (maybe an "output" method?) and check the results.

Comments

0

Simple minimalistic inheritance library: (2kb minified) https://github.com/haroldiedema/joii

It basically allows you to do the following (and more):

// First (bottom level)
var Person = new Class(function() {
    this.name = "Unknown Person";
});

// Employee, extend on Person & apply the Role property.
var Employee = new Class({ extends: Person }, function() {
    this.name = 'Unknown Employee';
    this.role = 'Employee';

    this.getValue = function() {
        return "Hello World";
    }
});

// 3rd level, extend on Employee. Modify existing properties.
var Manager = new Class({ extends: Employee }, function() {

    // Overwrite the value of 'role'.
    this.role = this.role + ': Manager';

    // Class constructor to apply the given 'name' value.
    this.__construct = function(name) {
        this.name = name;
    }

    // Parent inheritance & override
    this.getValue = function() {
        return this.parent.getValue().toUpperCase();
    }
});

// And to use the final result:
var myManager = new Manager("John Smith");
console.log( myManager.name ); // John Smith
console.log( myManager.role ); // Manager

console.log( myManager.getValue() ); // HELLO WORLD

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.