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();
2
  • 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. Commented Feb 20, 2015 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. Commented Feb 20, 2015 at 19:15

5 Answers 5

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.

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

Comments

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.

2 Comments

What do you mean by.. "own properties are copied with Shape.call(this); which is again can be undesirable."
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.
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.

4 Comments

actually, with the second approach, the way its written, the Super constructor gets called twice, once by new'ing it and second by Shape.call
@nuway I noticed that and made and of it.
great way to power phrase my answer btw
@nuway I didn't read your answer until after I finished mine.
0

Object.create does not execute the constructor.

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

5 Comments

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).
Object.create(object.prototype) , we need to set back the constructor property? but basically it doesnt impact any thing
Could this line be omitted? Rectangle.prototype.constructor = Rectangle; What does it do?!
refer to my answer on this post @clarkk stackoverflow.com/questions/28159784/…
if you do new Rectangle() it will still run rectangle orignal function but Rectangle.protoype.constructor() will call Shape() @clarkk
-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);.

1 Comment

Probably someone found your answer hard to understand. Maybe you need to expand it a little.

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.