Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

instanceof operator returns false on subsequent change to inheritance chain

When a prototype is set on a constructor function, the instanceof operator only returns true until the prototype is changed. Why?

function SomeConstructorFunction() {
}

function extendAndInstantiate(constructorFn) {
    constructorFn.prototype = {}; //Can be any prototype
    return new constructorFn();
}

var child1 = extendAndInstantiate(SomeConstructorFunction);
console.log(child1 instanceof SomeConstructorFunction); //true

var child2 = extendAndInstantiate(SomeConstructorFunction);
console.log(child1 instanceof SomeConstructorFunction); //false
console.log(child2 instanceof SomeConstructorFunction); //true
like image 855
Stephen Avatar asked Mar 19 '13 14:03

Stephen


2 Answers

Prototypal inheritance can be a bit of a brain bender. I've already written a simple explanation for prototypal inheritance in the following answer and I suggest you read it: https://stackoverflow.com/a/8096017/783743

Now to answer your question. Consider the following function:

function F() {}

I can create an instance of F using new as follows:

var f = new F;

As expected f instanceof F returns true. This is because the instanceof operator checks for F.prototype in the prototype chain of f and returns true if it is there. See the answer I linked to above.

Now say I create a new function G as follows and set F.prototype to G.prototype:

function G() {}

F.prototype = G.prototype;

If I now evaluate f instanceof F again false is returned. This is because F.prototype is no longer in the prototype chain of f (remember that F.prototype is now G.prototype).

Now let's create a new instance of F:

var g = new F;

If you evaluate g instanceof G it'll return true even though g = new F. This is because G.prototype exists in the prototype chain of g.

This is not a bug. It's the way JavaScript works. In fact we can exploit this feature to create some really interesting functions: Instantiate JavaScript functions with custom prototypes

like image 187
Aadit M Shah Avatar answered Oct 05 '22 05:10

Aadit M Shah


constructorFn.prototype = {}; //Can be any prototype

Not true!

constructorFn.prototype = Object.prototype;

Or any other native prototype will make them all true.

The reason is that the first time that you're calling extendAndInstantiate(), you're setting the SomeConstructorFunction's prototype to something (here an empty object {}). For child1, instanceOf will only return true while SomeConstructorFunction's prototype is that exact instance of {}. When you run extendAnInstantiate() a second time, you're changing SomeConstructorFunction's prototype to a different instance of {}, so child2 will be an instanceOf that new {}, but child1 is still an instanceOf of the old {}, so child1 will return false, while child2 returning true. If you set the prototype to a common prototype, such as Object.prototype or Array.prototype, it will always return true.

Another way to illustrate this is to pull {} out of the function, and assign it to a variable obj.

var obj = {};
function extendAndInstantiate(constructorFn) {
    constructorFn.prototype = obj;
    return new constructorFn();
}

Now this will always return true, because you aren't creating a new {} every time you run the function.

like image 23
MattDiamant Avatar answered Oct 05 '22 05:10

MattDiamant