2

Whats the difference between these two approaches?

First approach

// Shape - superclass
function Shape() {
    this.x = 0;
    this.y = 0;
}

// superclass method
Shape.prototype.move = function(x, y) {
    this.x += x;
    this.y += y;
};

// Rectangle - subclass
function Rectangle() {
    Shape.call(this);
}

// subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;

var rect = new Rectangle();

Second approach

// Shape - superclass
function Shape() {
    this.x = 0;
    this.y = 0;
}

// superclass method
Shape.prototype.move = function(x, y) {
    this.x += x;
    this.y += y;
};

// Rectangle - subclass
function Rectangle() {
    Shape.call(this);
}

// subclass extends superclass
Rectangle.prototype = new Shape();

var rect = new Rectangle();
Adrian Cid Almaguer
  • 7,815
  • 13
  • 41
  • 63
clarkk
  • 27,151
  • 72
  • 200
  • 340
  • Main disadvantage of the second approach is that it requires one more extra invocation of `new Shape`. This can be avoided with intermediate empty function, but anyway pattern #1 is preferred. – dfsq Feb 20 '15 at 19:01
  • Also, `Object.create` is only copying `Shape.prototype` over to `Rectangle.prototype`. If you set `Rectangle.prototype` to `new Shape()`, it will also have the privileged values `x` and `y` in its prototype, which you probably don't want. – Frank Feb 20 '15 at 19:15

5 Answers5

2

I see two differences: one big, one small.

The small difference is that in the second approach, Rectangle prototypes won't have their constructor property set properly. It will point to Shape, rather than Rectangle. This is an extremely common mistake in JavaScript OO approaches, and most of the time you can get away with it because people don't use the constructor property for very much, but it's there.

The big difference is the additional call to Shape() in the second constructor. In your particular example, things work out more or less OK: Rectangle.prototype has some extra properties (x and y, both of which equal zero) that you probably didn't intend for it to have, but these will get overshadowed by the corresponding properties in your Rectangle instances anyway. Again, the kind of extremely common but tiny bug that works often enough that you can get away with it.

The problem with the second method is that it only works when the superclass constructor doesn't need any arguments. Your example happens to fit that description, because all it does is initialize certain properties to default values. But there exist use cases where that setup isn't really appropriate, and the second method can't be used in those cases.

The Spooniest
  • 2,863
  • 14
  • 14
1

The primary difference is that in the second approach, the Shape constructor is called only once. In the first approach, the Shape constructor is not called, instead Object.create(Shape.prototype) creates a new object from the prototype. The parent constructor can then be called once per child construction with Shape.call.

You can still call Shape.call for each subclass, as in the second approach, but it results in an extra call to the constructor during the initial prototype creation.

Also, using the first approach with Object.create causes the constructor property of the prototype to be overwritten, so Rectangle.prototype.constructor = Rectangle; is used to reset the constructor property.

Alexander O'Mara
  • 58,688
  • 18
  • 163
  • 171
1

Main disadvantage of the second approach is that it requires one more extra invocation of Shape constructor in order to create an object to be used as prototype for Rectangle. There are different way to avoid it, for example with intermediate empty function:

function Rectangle() {
    Shape.call(this);
}

function f() {}
f.prototype = Shape.prototype;

Rectangle.prototype = new f();

As you can see, this is obviously very clumsy.

For this reason patter #1 using Object.create is preferred as more convenient and efficient.

However note that both snippets have another problem: own properties are copied with Shape.call(this); which is again can be undesirable.

dfsq
  • 191,768
  • 25
  • 236
  • 258
  • 1
    What do you mean by.. "own properties are copied with Shape.call(this); which is again can be undesirable." – clarkk Feb 20 '15 at 19:30
  • `Shape.call(this)` serves the only purpose - create own properties of the `Rectangle` instance object. Basically this line invokes `Shape` in context of the `Rectangle` which makes it gain own properties defined in `Shape`. But the problem is that parent constructor function may perform operations that are not desirable to be executed when `Rectangle` instance is created. – dfsq Feb 20 '15 at 19:34
0

Object.create does not execute the constructor.

calling with New is equivalent to Object.create(object.prototype) + running the constructor function.

A.B
  • 20,110
  • 3
  • 37
  • 71
  • Not quite, the `constructor` property will be different if you use `Object.create` then manually call the `constructor` on the object (sorry I deleted this the first time, I was double-checking something). – Alexander O'Mara Feb 20 '15 at 19:29
  • Object.create(object.prototype) , we need to set back the constructor property? but basically it doesnt impact any thing – A.B Feb 20 '15 at 19:34
  • Could this line be omitted? `Rectangle.prototype.constructor = Rectangle;` What does it do?! – clarkk Feb 20 '15 at 19:41
  • refer to my answer on this post @clarkk http://stackoverflow.com/questions/28159784/in-javascript-inheritance-via-b-prototype-new-a-why-does-one-need-to-set-b-p/28159807#28159807 – A.B Feb 20 '15 at 19:42
  • if you do new Rectangle() it will still run rectangle orignal function but Rectangle.protoype.constructor() will call Shape() @clarkk – A.B Feb 20 '15 at 19:47
-1

the biggest practical difference is that when you use new, the super class's constructor will be invoked, without need for Shape.call(this);.

nuway
  • 2,324
  • 4
  • 27
  • 48
  • 3
    Probably someone found your answer hard to understand. Maybe you need to expand it a little. – dfsq Feb 20 '15 at 19:14