简体   繁体   中英

javascript object inheritance - subclass and superclass

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();

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 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.

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.

Object.create does not execute the constructor.

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM