1

I have two chunks of code and they seem to work the same. The only difference between the two is one has Object.create() and the other does not. Am I missing some potential consequence of not using it?

First Chunk, does not use Object.create:

function Rectangle(width, height) {
  this.height = height;
  this.width = width;
}

Rectangle.prototype.area = function() {
  return this.width * this.height;
};

function Square(side) {
  Rectangle.call(this, side, side);
}

Square.prototype = Rectangle.prototype;

Square.prototype.constructor = Square;

var sq = new Square(5);
console.log(sq.area())

Second Chunk, uses Object.create:

function Rectangle(width, height) {
  this.height = height;
  this.width = width;
}

Rectangle.prototype.area = function() {
  return this.width * this.height;
};

function Square(side) {
  Rectangle.call(this, side, side);
}

Square.prototype = Object.create(Rectangle.prototype);

Square.prototype.constructor = Square;

var sq = new Square(5);
console.log(sq.area())
Paolo
  • 1,557
  • 3
  • 18
  • 28

2 Answers2

2

They're not the same. With this line, the two constructors' prototypes are the same object.

Square.prototype = Rectangle.prototype;

This means that any method on a Square will also appear on a Rectangle. So use Object.create() to assign a new object that inherits from Rectangle.prototype. That way methods will be added to that object, and not to Rectangle.prototype.

six fingered man
  • 2,460
  • 12
  • 15
1

In case you want to skim the post, I marked information directly related to your example with italics.

Prototypal inheritance

The prototypal inheritance works as follows:

  1. Each object o has a prototype o.__proto__.
  2. When you request a property p on object o, then:
    1. If the object o has property p, o[p] is returned.
    2. Otherwise:
      1. If o.__proto__ !== null, then o.__proto__[p] is returned.
      2. Otherwise, undefined is returned.

This behaviour is recursive, creating a so-called prototype chain. See the example code below:

var o1 = { a: 1 },
    o2 = { b: 2 },
    o3 = { c: 3 };
o2.__proto__ = o1;
o3.__proto__ = o2;
o3.a + o3.b + o3.c == 6;   // true

This is basically all there is to prototypes. It ought to be noted that __proto__ is a deprecated way of accessing the prototype. ECMAScript provides three other (standard) ways:

  • the prototype property of functions,
  • the Object.create() method,
  • the Object.getPrototypeOf() and Object.setPrototypeOf() methods.

Let us now look at these.

The prototype property of functions

The basics

Up to ECMAScript 5, the prototype property of functions was the only way to work with prototypes. It works as follows:

  1. Each function f has a prototype property.
  2. When new f(arg1, arg2, ..., argN) is invoked, then:
    1. An empty new object o is created, where o.__proto__ == f.prototype.
    2. The function f is invoked with arguments arg1, arg2, ..., argN in the context of o.
    3. If the function f doesn't return any value, then o is returned.

This behaviour basically mimics classical inheritance. You fill the f.prototype object with methods that objects obtained by calling new f will inherit and f itself serves as a constructor.

The prototype.constructor property

As far as constructors are concerned, the f.prototype object contains by default a f.prototype.constructor property, which equals to f. This can be used to find out the object's constructor either by checking out the constructor property directly or by using the instanceof syntactic sugar:

var o = new f;
o.constructor == f;        // true
o instanceof f;            // true

In your first example, you changed the prototype.constructor of both Square and Rectangle to Square. Therefore:

var o = new Rectange;
o instanceof Rectangle;    // false
o instanceof Square;       // true

This usually isn't what you want.

Extending the prototypes of built-in objects

Extending the prototype object of built-in functions / constructors (String, Array, Number, RegExp, ...) can be a useful application of this principle:

Array.prototype.last = function() {
  return this[this.length - 1];
};

[ 1, 2, 3, 4, 5 ].last();  // 5

The Object.create() method

The Object.create() method is a part of the ECMAScript 5 and allows you to create new objects with specified prototypes without having to create constructor functions. This is the initial __proto__ example code rewritten using the Object.create() method:

var o1 = Object.create(null, {
      a: {
        writable: true,
        configurable: true,
        value: 1
      }
    }), o2 = Object.create(o1, {
      b: {
        writable: true,
        configurable: true,
        value: 2
      }
    }), o3 = Object.create(o2, {
      c: {
        writable: true,
        configurable: true,
        value: 3
      }
    });
o3.a + o3.b + o3.c == 6;   // true

Setting the object's properties using the second argument of Object.create() can get quite wordy. If you don't need the flexibility ECMAScript 5 offers (creating immutable properties, properties with setters and getters, ...), you can omit the second argument and fill the object with values as usual:

var o1 = Object.create(null);
o1.a = 1;    
var o2 = Object.create(o1);
o2.b = 2;
var o3 = Object.create(o2);
o3.c = 3;
o3.a + o3.b + o3.c == 6;   // true

In your second example, you changed the prototype property of Square to Object.create(Rectangle.prototype) and then set Square.prototype to Rectangle.prototype. Since the Object.create() method returns an empty object with, you don't override anything in the Rectangle's prototype by assigning to it, so:

var o1 = new Rectange;
var o2 = new Square;
o1 instanceof Rectangle;    // true
o2 instanceof Square;       // true

The Object.getPrototypeOf() and Object.setPrototypeOf() methods

The Object.getPrototypeOf() and Object.setPrototypeOf() methods are a part of the ECMAScript 6 draft and are just a getter and a setter to the __proto__ attribute. This is the initial __proto__ example code rewritten using the Object.setPrototypeOf() method:

var o1 = { a: 1 },
    o2 = { b: 2 },
    o3 = { c: 3 };
Object.setPrototypeOf(o2, o1);
Object.setPrototypeOf(o3, o2);
o3.a + o3.b + o3.c == 6;   // true
Witiko
  • 3,167
  • 3
  • 25
  • 43