1

Suppose we have this constructor:

var Foo = function(){
   this.x = "y";
}

Foo and Foo.prototype.constructor evaluate to the same function, yet Foo.prototype.constructor = function(){this.x="z"} doesn't seem to change the result of new Foo(). It does however change the result of

var i = new Foo();
i.constructor; // evals to function (){this.x = "z"}

What's going on here? I don't plan on using this for anything, I'm just curious about the language.

AlexMA
  • 9,842
  • 7
  • 42
  • 64
  • I'm unfamiliar with saying something = new function () {this.x = "z"}... is that actually valid syntax? – kinakuta May 31 '12 at 15:02
  • no.. that was a mistake. I'll fix it. – AlexMA May 31 '12 at 15:03
  • There are many related questions you may want to read: http://stackoverflow.com/search?q=javascript+constructor+property –  May 31 '12 at 15:05

2 Answers2

3

The constructor property of the prototype property of a function is meant to point back to the function so that you can ask an object what constructed it. It's set up automatically as part of creating the function object (See Section 13.2 of the spec [or here for a more up-to-date version].) As you've seen, you can override the constructor property on the Foo.prototype if you like, to change that, but by default that's what it's for. For years the JavaScript specification only said that the constructor property would be there and have a given default value (function Foo() { } will mean that, by default, Foo.prototype.constructor is Foo.). But starting in ES2015, that changed, and various operations in the specification now actually use the constructor property, such as here, here, here, and here.

(Note that the following was written before ES2015's class feature was added. The below is how you'd do this in ES5 and earlier. In ES2015+, if you're doing constructor functions and inheritance hierarchies, there's no good reason to do the below; just use class. [If you don't use constructor functions to build inheritance hierarchies — and you don't have to, there are other ways to do them in JavaScript — you wouldn't do the below or use class.])

There's a good reason you can override it, which relates to inheritance. Suppose you want to have a Base constructor that creates base objects, and a Derived constructor that creates derived objects with the features of Base plus the additions/modifications of Derived. The usual (though not to my mind ideal) way you see that done (absent helper scripts) is:

function Base() {
}
Base.prototype.foo = function() {
    console.log("I'm Base#foo");
};

function Derived() {
}
Derived.prototype = new Base(); // So we get all the `Base` stuff
Derived.prototype.bar = function() {
    console.log("I'm Derived#bar");
};

var d = new Derived();
d.foo(); // "I'm Base#foo"
d.bar(); // "I'm Derived#bar"

The problem is that now, d.constructor === Base rather than Derived. So being able to fix that is important:

...
Derived.prototype = new Base();                           // So we get all the `Base` stuff
Object.defineProperty(Derived.prototype, "constructor", { // Fix up
    value: Derived,
    writable: true,
    configurable: true
});
...

(Side note: All of this plumbing — and complexity around supercalls — is why ES2015+ have class syntax.)


Note that the above is not meant to be an ideal way to set up inheritance hierarchies. It's what you usually see, but as I said above, not ideal. Just for completeness, in an environment limited to ES5 syntax, this is better:

function Base() {
}
Base.prototype.foo = function() {
    console.log("I'm Base#foo");
};

function Derived() {
    Base.call(this); // So Base sets up its stuff
}
Derived.prototype = Object.create(Base.prototype); // So we get all the `Base` stuff
Object.defineProperty(Derived.prototype, "constructor", {
    value: Derived,
    writable: true,
    configurable: true
});
Derived.prototype.bar = function() {
    console.log("I'm Derived#bar");
};

var d = new Derived();
d.foo(); // "I'm Base#foo"
d.bar(); // "I'm Derived#bar" 

...where in a pre-ES5 environment, you use a shim/polyfill for Object.create. But again, I don't do this directly (and don't recommend it), I use helper scripts so it's declarative and repeatable.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • It's cleaner to use `Derived.prototype = Object.create( Base.prototype )` (With shimmed `Object.create`, it's all the same), which doesn't invoke the constructor which might have `.bind` calls and such. Inheritance is still properly established and instanceof etc keeps working. This is also emphasized at [MDN](https://developer.mozilla.org/en/JavaScript/Guide/Inheritance_Revisited) (Though they still use `new A` which is bad) – Esailija May 31 '12 at 15:08
  • @Esailija: Indeed it is. And that's basically what my script does. As I said above: *"...The usual (though not to my mind ideal) way..."* Sadly, one sees this "usual" way far too often. – T.J. Crowder May 31 '12 at 15:13
  • Heh, I didn't even read that :P By `your script` are you referring to lineage? – Esailija May 31 '12 at 15:14
  • @Esailija: Yes. You won't approve, because it's not using an ES5 shim (although it basically does do `Object.create`) and because it looks classy. :-) – T.J. Crowder May 31 '12 at 15:17
  • No no, I prefer classy. You wouldn't catch me writing `var obj = Object.create( Obj ).constructor(1)` over `var obj = new Obj(1)` :P – Esailija May 31 '12 at 15:27
2

.prototype.constructor is just a helper property so that the objects created can easily refer the constructor with this.constructor, or as in your case i.constructor.

Setting it arbitrarily to something else doesn't have any effect except on code that expects to get the constructor of an object with obj.constructor:

if( obj.constructor === Foo ) {
 //It's a Foo
}

So it's a good convention just to leave it be.

The es5shim relies on .constructor.prototype to shim Object.getPrototypeOf

Esailija
  • 138,174
  • 23
  • 272
  • 326