How does instanceof work in JavaScript?
Asked Answered
E

3

14

In the following code sample both checks of obj2 and obj3 at the end with instanceof return true even if the ways there were constructed are different and the results of returning name property are different.

var Obj1 = function() {
    this.name = "foo1";
};
Obj1.prototype.name = "foo1onProt";
var obj1 = new Obj1();

var Obj2 = function() {};
Obj2.prototype = new Obj1();
Obj2.prototype.constructor = Obj2;
var obj2 = new Obj2();

var Obj3 = function() {};
Obj3.prototype = Object.create(Obj1.prototype);
Obj3.prototype.constructor = Obj3;
var obj3 = new Obj3();

console.dir(obj1);
console.log("obj1.name: " + obj1.name);

console.dir(obj2);
console.log("obj2.name: " + obj2.name);

console.dir(obj3);
console.log("obj3.name: " + obj3.name);

console.log("obj2 instanceof Obj1: " + (obj2 instanceof Obj1));
console.log("obj3 instanceof Obj1: " + (obj3 instanceof Obj1));

Result of the run in Chrome:

Obj1
  name: "foo1"
  __proto__: Object
    constructor: function () {
    name: "foo1onProt"
    __proto__: Object
obj1.name: foo1
Obj2
  __proto__: Obj1
    constructor: function () {}
    name: "foo1"
    __proto__: Object
      constructor: function () {
      name: "foo1onProt"
      __proto__: Object
obj2.name: foo1
Obj3
   __proto__: Object
   constructor: function () {}
   __proto__: Object
     constructor: function () {
     name: "foo1onProt"
     __proto__: Object
obj3.name: foo1onProt

obj2 instanceof Obj1: true
obj3 instanceof Obj1: true

What is the best way to recognize that obj2 and obj3 are different? How does actually instanceof work?

Esoterica answered 27/6, 2014 at 13:40 Comment(3)
You create two different objects, both with Obj1 as their base - and then you ask them if they are instances of Obj1. Well, yes, they both are. If you want to know if they are different, then just use the === operator.Favors
@Favors I see your reply is from many years ago, however I have never seen an approach using strict equality to directly compare JavaScript objects. Would you mind expanding on what you meant by your comment above regarding ‘objA === objB‘ ? developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Paddle
For objects, the === operator returns true only if the two objects compared are the same object. While you would not normally compare an object to itself, this can be useful if you want to figure out which of object, out of a known set, you have a reference to.Favors
P
30

What is the best way to recognize that obj2 and obj3 are different?

That will depend a great deal on what you're doing with them. One way would be to use instanceof Obj2 and instanceof Obj3. Since both objects were created with Obj1.prototype in their prototype chain, it makes sense that they identify as being an instance of what we would call the supertype in class-based OOP.

How does actually instanceof work?

The short version

obj instanceof F looks to see if the object referenced by F.prototype is anywhere in obj's prototype chain. It doesn't use constructor at all.

More details

This is covered in the spec by §11.8.5 - The instanceof Operator, which says (indirectly, via §8.6.2) that it calls the [[HasInstance]] internal method of the function object, passing in the object we're testing. Function's [[HasInstance]] (in §15.3.5.3) says that it gets the object reference from the function's prototype property and then returns true if that object is anywhere in the target object's prototype chain, false if it doesn't.

It doesn't use constructor (nothing in JavaScript itself does, in fact) — and if you think about it, it can't, because an object's constructor property can only point at one function, but an object can be instanceof multiple functions — for instance, in the case of pseudo-classical inheritance:

function F1() {}

function F2() {
  F1.call(this);
}
F2.prototype = Object.create(F1.prototype);
F2.prototype.constructor = F2;

var obj = new F2();
console.log(obj instanceof F1); // true
console.log(obj instanceof F2); // true

Both are true because the two objects referenced by F1.prototype and F2.prototype are both in obj's prototype chain.

instanceof being true doesn't necessarily mean that obj was created by a call to F, either directly or indirectly; it just indicates there's a vague link between them (F.prototype refers to an object that's also in obj's prototype chain). It usually means F was involved in creating the object, but there's no guarantee.

For instance:

function F() {}
var obj = Object.create(F.prototype);
console.log(obj instanceof F); // true

Note that F wasn't called to create the object, at all.


Or perhaps more clearly and/or dramatically:

function F() {}
var p = {};
var obj = Object.create(p);
console.log(obj instanceof F); // false
F.prototype = p;
console.log(obj instanceof F); // true

There's also this unusual, but entirely possible, version:

function F1() {}
function F2() {}
F1.prototype = F2.prototype = {};
var obj = new F1();
console.log(obj instanceof F2); // true

Or this one:

function F1() {}
function F2() {}
var obj = new F2();
console.log(obj instanceof F1); // false
F1.prototype = F2.prototype;
console.log(obj instanceof F1); // true
Pazia answered 9/4, 2015 at 15:40 Comment(5)
@VLAZ - Thanks, I didn't know any of my answers still used that. I'll have to go hunting...Pazia
oh, right...I also meant to write a comment to notify you. I forgot about that during editing. :) Also, I tried finding the repo but seems it's gone, I assume because it's not needed any more.Enamor
@Enamor - No need to leave a comment, SO notifies you when your answer is edited. :-) I don't recall removing that repo, but it seems more likely that I did than that github lost it. :-D Ugh, data explorer says I have 349 of these to fix... ;-( At two minutes per, that's nearly 12 hours of work. I'll do 5-10 a day for a month or two...Pazia
Maybe you can automate it :) I can also lend a hand every once in a while. I've recently taken to updating some older posts mostly with making snippets runnable. I'm still exploring ways to find which posts to update, I'm mostly browsing and reviewing entire Q&As that catch my eye. So, it's unguided and sporadic for now - your posts give a more direct goal I can work towards.Enamor
@Enamor - That's very kind! I can do it in dribs and drabs, but if you want to pitch in, here's the list, just change any you do to strikethrough text (please don't delete them). If you see any with my old-style ASCII-art using - instead of −, could you add a note? I'll go fix them. :-) But again, only if you really want to, I'll get to them otherwise. (I've already done a few that aren't on that list, so it's not 349 anymore...)Pazia
K
3

Most simply: obj instanceof constructor yields true when obj has constructor's prototype in it's constructor/prototype chain. In other words, your asking your engine whether obj can be treated like an instance of constructor / whether obj behaves like a constructor object.

There is a small handful of syntaxes that allow you to put constructor's prototype in obj's prototype chain. Any and all of them will cause obj instanceof constructor to be true. In your examples, both obj2 and obj3 have Obj1 in their prototype chain.

So, when you ask your javascript engine whether either obj2 or obj3 behave like an instance of Obj1, JavaScript assumes true -- the only case wherein they wouldn't is if you've overridden Obj1's behavior down the line.

Kosiur answered 27/6, 2014 at 13:45 Comment(2)
@FrédéricHamidi Good distinction.Kosiur
This is wrong answer. The answer of T.J. Crowder is correct.Jess
D
0
function F1() {}

function F2() {
  F1.call(this);
}
F2.prototype = Object.create(F1.prototype);
F2.prototype.constructor = F2;

var obj = new F2();
console.log(obj instanceof F1); // true
console.log(obj instanceof F2); // true
Dispeople answered 27/3, 2022 at 5:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.