4

I have the following JavaScript code

function Parent() {
}

function Child() {
}

Child.prototype = Object.create(Parent.prototype);

Note the absence of the statement

Child.prototype.constructor = Child;

My understanding is that as the constructor property has not been set the instanceof checks should fail for new instances of Child class.

var child = new Child();
child instanceof Child; //Should give false

I verified that the constructor is incorrectly set enter image description here

But when I run child instanceof Child it gave me true

enter image description here

But it should be false as constructor property is not set on Child's prototype to be Child.

Environment

Google Chrome Version 48.0.2564.109 (64-bit)
Ubuntu 14.04 LTS
Aseem Bansal
  • 6,722
  • 13
  • 46
  • 84
  • Related: http://stackoverflow.com/questions/8453887/why-is-it-necessary-to-set-the-prototype-constructor – Felix Kling Feb 21 '16 at 15:41
  • @Bergi Although the answer to that question can be related to this question , IMO the questions do differ. – Aseem Bansal Feb 21 '16 at 15:49
  • @AseemBansal: The SO definition of "duplicate" is (perhaps surprisingly) not about the *questions* being the same, but whether the *answer* to an earlier question also answers this question. (It's also not pejorative; duplicates improve the odds of people finding the answer, by wording the questions differently.) I think Bergi's right. (He's good at duplicates.) – T.J. Crowder Feb 21 '16 at 15:58

1 Answers1

7

My understanding is that as the constructor property has not been set the instanceof checks should fail for new instances of Child class.

The constructor property isn't used by instanceof at all. In fact, until ES2015 (aka ES6), the constructor property wasn't used for anything in JavaScript itself. It was defined as existing on the default objects the runtime assigns to the prototype property on functions, but not used.

Let's look at how instanceof works. Consider:

o instanceof Foo

As of ES2015, instanceof will check to see if Foo implements an internal operation called @@hasInstance and, if so, it will use that operation to ask Foo whether o is an instance.

In ES5 and earlier (or if Foo doesn't have that internal operation), instanceof will look to see if the object Foo.prototype points to is anywhere on o's prototype chain. (If "prototype chain" is not a familiar term, see the * at the end of the answer and then come back.) If so, it returns true; if not, it returns false.

E.g., here's a conceptual implementaton of instanceof, hand-waving away some details:

function isAnInstance(obj, func) {
    // Start: ES2015+ part
    const hasInstance = func[Symbol.hasInstance];
    if (hasInstance) {
        return hasInstance.call(func, obj);
    }
    // End: ES2015+ part
    // Start: The OrdinaryHasInstance specification operation
    for (let p = Object.getPrototypeOf(obj); p; p = Object.getPrototypeOf(p)) {
        if (p === func.prototype) {
            return true;
        }
    }
    return false;
    // End: The OrdinaryHasInstance specification operation
}

The functions built into JavaScript do have @@hasInstance (they inherit it from Funtion), but all it does is the OrdinaryHasInstance operation shown above. See the specification here, here, and here.

We can see that constructor isn't involved from your question, and also from this simple demonstration:

var p = {};
var o = Object.create(p);
var Foo = function() {};
Foo.prototype = p;
console.log(o instanceof Foo);                // true
console.log(o.hasOwnProperty("constructor")); // false
console.log(
    Object.getPrototypeOf(o).constructor === Object
);                                            // true

Note that:

  1. o wasn't created via Foo.
  2. In fact, Foo didn't even exist until after o was created.
  3. o doesn't have its own constructor property.
  4. o's prototype's constructor property is Object, not Foo.

...and yet instanceof says "Yep, looks like it's a Foo." :-) Purely because the object Foo.prototype points to is also on o's prototype chain.


* "prototype chain"

You clearly know that objects in JavaScript have prototypes from which they inherit properties. Those prototypes are objects, and so they have prototypes. So you get a "chain" of prototypes.

Consider a two-level (arguably three-level) inheritance hierarchy, here in ES5:

function Base() {
}

function Derived() {
    Base.call(this);
}
Derived.prototype = Object.create(Base.prototype);
Derived.prototype.constructor = Derived;

...or in ES2015:

class Base {
}
class Derived extends Base {
}

Now we use it:

var d = new Derived();

(Where you see "the d object" and similar in the below, I do of course mean "the object d refers to" — but that's really verbose.)

Now, the d object's prototype is Derived.prototype. Derived.prototype's prototype is Base.prototype. Base.prototype's prototype is Object.prototype. Object.prototype doesn't have a prototype (its [[Prototype]] internal slot is null).

Those objects are the prototype chain underlying d, and they mean that d is instanceof Object, instanceof Base, and instanceof Derived.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Could you please rephrase this - " with the object that the prototype property of the function you used"? I am not able to understand its meaning. Perhaps a simple example? – Aseem Bansal Feb 21 '16 at 15:44
  • 1
    @AseemBansal: You know, I wrote that sentence and thought "Hmmm, clear as mud?" Let's see if I can fix that. :-) – T.J. Crowder Feb 21 '16 at 15:46
  • Wait, where does ES6 use the `.constructor` property for anything? – Bergi Feb 21 '16 at 15:49
  • 1
    @Bergi: It's involved in the non-sugar aspects of `class`, primarily [here](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-speciesconstructor) and [here](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-arrayspeciescreate). I think it's part of what makes it possible to subclass arrays and promises. For instance, [`Promise.prototype.then`](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-promise.prototype.then) uses *SpeciesConstructor*, which uses the `constructor` property on the promise. – T.J. Crowder Feb 21 '16 at 15:55
  • @AseemBansal: I've reworded, and had already added an example. Does that make it clear? – T.J. Crowder Feb 21 '16 at 16:09
  • @T.J.Crowder: Ah, right, I forgot about species. Though there still is some discussion on how exactly that should look like iirc. – Bergi Feb 21 '16 at 16:17
  • @Bergi: Well, it's in the spec now, so... :-) – T.J. Crowder Feb 21 '16 at 16:18
  • Yes it is clear now. Just had to start a bounty about why MDN and Udacity course are telling to explicitly set this property. In case you are interested http://stackoverflow.com/questions/8453887/why-is-it-necessary-to-set-the-prototype-constructor – Aseem Bansal Feb 21 '16 at 16:19
  • …so they gonna change it with ES7. Or ES8. Whenever it fits them :-) Edit: Or maybe I misremember and it was only about the species symbol usage, not the constructor property. – Bergi Feb 21 '16 at 16:19
  • @Bergi: Maybe. You know they don't jerk things around like that lightly... – T.J. Crowder Feb 21 '16 at 16:22
  • 1
    @AseemBansal: No, the answers there are not outdated. Maybe also have a look at [What it the significance of the Javascript constructor property?](http://stackoverflow.com/q/4012998/1048572) - it's convenience. – Bergi Feb 21 '16 at 16:22
  • @Bergi Thanks for the links but as per the explanation in the comments here are the answers there not outdated? – Aseem Bansal Feb 21 '16 at 16:31
  • 1
    @AseemBansal: No, what TJ reminded me of is a very special case, and doesn't apply to ES5 subclassing where you need to set the `.constructor` explicitly if *you* need it. If you're subclassing arrays or promises, you have to use ES6 `class` syntax anyway which sets the property automatically. – Bergi Feb 21 '16 at 16:38
  • @Bergi If I could I would give this comment the bounty. This cleared the whole confusion. Many thanks. – Aseem Bansal Feb 21 '16 at 16:43