Dexy_Wolf
Dexy_Wolf

Reputation: 999

Use of prototype constructor in JS

Can someone explain to me use of Me.prototype.constructor = Me; and why is needed, when this code is working and without it?

In code prototype object is created on Me object and it is instantiated and replaced old prototype object. Why do I need to point to Me constructor in a given up code?

function Me(){
    this.name = 'Dejan';

}

function You(){
    this.name = 'Ivan';
}

Me.prototype = new You();

somebody = new Me();

Me.prototype.constructor = Me; // Why?

Me.prototype.foo = function(){
    alert('Proto Me!'); // It always fire up this alert, ether constructor is pointing to Me or not... !
}

You.prototype.foo = function(){
    alert('Proto You!');
}

somebody.foo();
alert(somebody.name); // Alert 'Dejan'

Upvotes: 10

Views: 1170

Answers (2)

Domenic
Domenic

Reputation: 112827

If you replace the line

Me.prototype.constructor = Me; // Why?

with

console.log(Me.prototype.constructor);
Me.prototype.constructor = Me; // Why?

you will find that before setting it, Me.prototype.constructor is You, since Me.prototype is an instance of You due to the line

Me.prototype = new You();

So, the line with the // Why? comment is necessary to "fix" this mistaken impression that you've given JavaScript by doing inheritance this way.


Essentially the problem comes about because you are trying to use prototypal inheritance to implement classical inheritance. Prototypal inheritance works on object instances, and has no concept of "classes" or even, really, "types," but JavaScript makes things extra confusing with the whole new, .constructor, and instanceof business.

A more prototypal way of doing this kind of thing is to eschew constructors in favor of power constructors, i.e. functions that return an object with the form you desire:

function begetPart(partNumber,  description) {
    return Object.create({}, {
        number: { value: partNumber },
        description: { value: description },
        describe: {
            value: function () {
                alert(this.description);
            }
        }
    });
}

function begetTire(partNumber, speed) {
    return Object.create(
        begetPart(partNumber, "A tire"),
        {
            speed: { value: speed },
            describe: {
                value: function () {
                    alert(this.description + "; speed = " + this.speed);
                }
            }
        }
    );
}

var genericPart = begetPart(1234, "A widget");
genericPart.describe(); // alerts "A widget"

var tire = begetTire(4567, "fast");
tire.describe(); // alerts "A tire; speed = fast"

Here we use Object.create to say "create an object instance based on this other object instance". The other instance is a new, empty object for begetPart and a new "part instance" with some properties pre-filled for begetTire.

This better reflects how JavaScript and prototypal inheritance actually works, since in prototypal inheritance object instances inherit from other object instances, without this whole "types" or "classes" idea getting in the way.

Upvotes: 5

chjj
chjj

Reputation: 14602

It's not needed, and it's not even needed for instanceof contrary to popular belief (instanceof internally checks the prototype chain and does not need a constructor property). Normally, constructor is inherently a non-enumerable property on a constructor's prototype. Thus giving any objects instantiated by that constructor, a non-enumerable constructor property, pointing to that constructor.

It's good to put it there if you need to, ideally non-enumerable. Some code will assume the existence of .constructor on objects.

In the code you posted, yes, when doing inheritance that way, it's necessary to reset constructor (if you want it there), because the object you instantiated to act as the child prototype has a constructor property pointing to the wrong constructor (its constructor).

In ES5, you would do:

Child.prototype = Object.create(Parent.prototype, {
  constructor: { value: Child, enumerable: false }
});

edit: Also, might be worth mentioning, when doing inheritance using the non-standard __proto__, it's not necessary to reset constructor because __proto__ merely specifies and object's prototype, which it to say, the object on which lookups will be performed when an own property doesn't exist. A new prototype will always have a property called constructor.

So in doing:

var child = function() {};
child.prototype.__proto__ = parent.prototype;

You do not have to set constructor because child.prototype's base constructor property is still there. If accessed, no prototype chain lookups need to be performed.

Upvotes: 11

Related Questions