adieu
adieu

Reputation: 714

What will go wrong if I don't set constructor correctly when subclassing in javascript?

I'm learning prototype based OOP in javascript recently. After reading lots of websites explaining the prototype mechanism in javascript, I settle down with this approach from Mozilla

Here are some snippets from Mozilla:

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

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

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

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

var rect = new Rectangle();

rect instanceof Rectangle // true.
rect instanceof Shape // true.

rect.move(1, 1); // Outputs, "Shape moved."

I understand that Rectangle.prototype = Object.create(Shape.prototype); will set the prototype chain properly so that instance of Rectangle will inherit methods from Shape.

But the Rectangle.prototype.constructor = Rectangle; line is a little bit confusing.

From this website I know that when we create a new object like function MyClass(){}, javascript will set its prototype to an empty object and the MyClass.prototype.constructor will point back to MyClass. So Rectangle.prototype.constructor = Rectangle; will fix the broken link since Object.create(Shape.prototype).constructor still points to Shape.

But if I remove this line, I will still get the following code running without a problem. I'm just feeling curious if there's any use case for the constructor attribute? What will go wrong if I just leave it pointing to the super class?

Upvotes: 3

Views: 222

Answers (3)

Karol
Karol

Reputation: 8053

In your code it doesn't make any difference, as you still create your object using Rectangle constructor (explicitly using the name of it: var rect = new Rectangle();).

The difference is only in the case, when you've lost reference to object's constructor, or you simply don't know what is it:

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

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

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

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

console.info("RECT");
var rect = new Rectangle;
console.info(rect);
console.info(rect instanceof Rectangle);
console.info(rect instanceof Shape);
//console.info(rect instanceof Square); TypeError - Square must be a function, but is still not initialized
rect.move(1, 1);

console.info("RECT2");
var rect2 = new rect.constructor();
console.info(rect2);
console.info(rect2 instanceof Rectangle);
console.info(rect2 instanceof Shape);
// console.info(rect2 instanceof Square); TypeError - Square must be a function, but is still not initialized
rect2.move(1, 1);

console.info("RECT3");
var Square = rect.constructor;
Square.prototype.move = function(x, y) { // just to make it different
    this.x *= x;
    this.y *= y;
    console.info(this.x + " " + this.y);
}

var rect3 = new Square();
console.info(rect3);
console.info(rect3 instanceof Rectangle);
console.info(rect3 instanceof Shape);
console.info(rect3 instanceof Square);
rect3.move(4, 4);

I commented out the line with setting the constructor. Uncomment it to see the difference in output. I have also added another way to use the constructor property - another inheritance example (Square based on Shape but with different move method).

So if you don't fix this link, in the case I showed, you will use Shape constructor, and creation of objects of the same type will become simply impossible. rect was of both types, but rect2 is not anymore.

So assuming, even if you don't need it, it is a very good practice to fix this link to the correct constructor, as someone working with your code in the future may need it.

EDIT:

constructor property doesn't affect anything in the object itself. It's set once object is created to save the reference to the function which created the object. If you will need it in the future than it makes sense to have a correct one. I don't see any other issues.

Upvotes: 2

vaibhav silar
vaibhav silar

Reputation: 3085

instanceOf will return true if the object is of the specified object type in its inheritance chain. It has nothing to do with constructor property.

We can use constructor property if we want to initialize property or execute default code at the time of creation of Object.

For example in your example, if we want to create Rectangle always in red color, we can use constructor of Rectangle as follows:

function Rectangle() {
  Shape.call(this); // call super constructor.
  this.color = "red";
}

Hope this clear your query.

Upvotes: -1

c.P.u1
c.P.u1

Reputation: 17094

Setting a property this way will make it enumerable(a for in statement would list the constructor property).

var rec = new Rectangle();
'constructor' in rec; //true

You should be using Object.defineProperty or pass in a second argument to Object.create.

Rectangle.prototype = Object.create(Shape.prototype, {constructor: {enumerable: false }});

var rec = new Rectangle();
'constructor' in rec; //

Please refer to this thread for more on the constructor property:

https://stackoverflow.com/a/4013295

Upvotes: 3

Related Questions