51

In some of my own older code, I use the following:

Object.prototype.instanceOf = function( iface )
{
 return iface.prototype.isPrototypeOf( this );
};

Then I do (for example)

[].instanceOf( Array )

This works, but it seems the following would do the same:

[] instanceof Array

Now, surely this is only a very simple example. My question therefore is:

Is a instanceof b ALWAYS the same as b.prototype.isPrototypeOf(a) ?

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
Steffen Heil
  • 4,286
  • 3
  • 32
  • 35
  • While you can always use `instanceof` (with constructors on the RHS), not all objects might inherit from `Object.prototype`. `Object.create(null) instanceof Something` and `({}).instanceOf({prototype:Something.prototype})` will work (and yield `false`) where the reverse would fail. – Bergi Nov 20 '13 at 01:08

3 Answers3

32

Yes, they do the same thing, both traverse up the prototype chain looking for an specific object in it.

The difference between both is what they are, and how you use them, e.g. the isPrototypeOf is a function available on the Object.prototype object, it lets you test if an specific object is in the prototype chain of another, since this method is defined on Object.prototype, it is be available for all objects.

instanceof is an operator and it expects two operands, an object and a Constructor function, it will test if the passed function prototype property exists on the chain of the object (via the [[HasInstance]](V) internal operation, available only in Function objects).

For example:

function A () {
  this.a = 1;
}
function B () {
  this.b = 2;
}
B.prototype = new A();
B.prototype.constructor = B;

function C () {
  this.c = 3;
}
C.prototype = new B();
C.prototype.constructor = C;

var c = new C();

// instanceof expects a constructor function

c instanceof A; // true
c instanceof B; // true
c instanceof C; // true

// isPrototypeOf, can be used on any object
A.prototype.isPrototypeOf(c); // true
B.prototype.isPrototypeOf(c); // true
C.prototype.isPrototypeOf(c); // true
Christian C. Salvadó
  • 807,428
  • 183
  • 922
  • 838
  • 1
    So, the only difference is, that I can use isPrototypeOf, if I have only the prototype, whereas I need the constructor for instanceof? (Making my function really identical to instanceof?) – Steffen Heil Mar 19 '10 at 12:45
  • 1
    Your text answer is useful, your code example is less. (It does only show equal aspects but no differences.) However it raises another question to me: That is ".constructor" for? I've seen such code in some places, but never wrote that myself and I don't seem to have needed it. (I usually had something like `C.prototype.clazz = C;` tough.) Why do people set constructor? – Steffen Heil Mar 19 '10 at 12:48
  • 1
    @Steffen Heil: the code written by CMS is clear enough I think, it uses the most simple and known (though it is not the most effective) way to inherit in JavaScript between constructors. Try to delete the line `B.prototype.constructor = B` and inspect the constructor of an instance of B: `alert((new B).constructor)` and you'll see the A function constructor. By doing that, he assures himself to find, as a constructor of all the instances of B, just B. – yodabar Jul 20 '12 at 18:24
  • @CMS Can you also say, `instanceof` is mainly for pseudo-classical inheritance, as it relies on `B.prototype`, and `B`, which is a "constructor". So what if `B.prototype` and `B` are not used, so there is no constructor, when the JavaScript program is written strictly in a prototypal inheritance way? In this case, then `animal.isPrototypeOf(woofie)` can be used – nonopolarity Feb 07 '13 at 22:42
  • AFAIK `instanceof` doesn't work when not using a constructor function, e.g. `Object.create`. Seems to have all kind of other problems as well: http://tobyho.com/2011/01/28/checking-types-in-javascript/ – rmoestl Jun 06 '16 at 09:25
  • Some interesting extra info about speed of both functions: https://lh5.googleusercontent.com/unnZizdyItYS7R2DDeHaGSLAmRpFwAng2rSoLJLfU1t2XGf7vTpS1bOeseiBqAdHWmYm7O3SPc8uncHNr7Iy8IRrwQT_1d6CQy4brLlQL0IQJ7sW0pBoHH8ourn0hRqBjj9efrti For more info: https://v8project.blogspot.fr/2017/08/v8-release-61.html?m=1 – godot Aug 11 '17 at 10:40
8

Is a instanceof b ALWAYS the same as b.prototype.isPrototypeOf(a) ?

No, a instanceof b will not always behave the same as b.prototype.isPrototypeOf(a).

CMS' answer pointed out that they differ in what they are (one is an operator and the other is a built-in method available on the Object.prototype object). This is correct, however there are also some special cases for which a instanceof b will result in a TypeError while b.prototype.isPrototypeOf(a) will work just fine and vice versa.

Difference #1

The right-hand side of instanceof is expected to be a constructor function.

If b is not a function:

  • a instanceof b will result in a TypeError.

  • b.prototype.isPrototypeOf(a) will work just fine.

const b = {
    prototype: {}
};
const a = Object.create( b.prototype );

console.log( b.prototype.isPrototypeOf(a) );    // true
console.log( a instanceof b );                  // TypeError: Right-hand side of 'instanceof' is not callable

Difference #2

When using b.prototype.isPrototypeOf(a), b.prototype should be inheriting from Object.prototype:

If b.prototype has not access to the Object.prototype.isPrototypeOf() method:

  • b.prototype.isPrototypeOf(a) will result in a TypeError.
  • a instanceof b will work just fine.

function B() {};
B.prototype = Object.create( null );

const a = new B();

console.log( a instanceof B );              // true
console.log( B.prototype.isPrototypeOf(a) ) // TypeError: B.prototype.isPrototypeOf is not a function

Difference #3

If the right-hand side of instanceof is a bound function, it is treated equivalently to its target function.

If b is a bound function:

  • a instanceof b will work just fine.
  • b.prototype.isPrototypeOf(a) will result in a TypeError (bound functions don't have a prototype property).

function B() {};
const BoundB = B.bind( null );
const a = new B();

console.log( a instanceof BoundB );              // true
console.log( BoundB.prototype.isPrototypeOf(a) ) // TypeError: Cannot read property 'isPrototypeOf' of undefined

Conclusion

  • If you are dealing with prototypal inheritance established through Object.create(), without the use of constructors, you should probably be using the Object.prototype.isPrototypeOf() method (indeed the use cases of instanceof are more restricted in that instanceof expects its right-hand side parameter to be a constructor function).
  • If you are dealing with constructors you will be slightly safer by using the instanceof operator (you will be able to cover bound functions as well as the cases where Object.prototype does not lie in the prototype chain of Constructor.prototype).
Oliver Sieweke
  • 1,812
  • 2
  • 14
  • 23
  • 1
    Note that the issue encountered in _Difference #2_ could also be avoided by using `Object.prototype.isPrototypeOf.call(B.prototype, a)` – Oliver Sieweke Jan 04 '19 at 16:39
  • see https://www.quora.com/Javascript-is-the-behavior-of-this-call-defined-Object-prototype-isPrototypeOf-call-a-b – Pacerier May 28 '19 at 18:32
2

Operator precedence and truthiness differ since one is an expression and the other is a method call. One thing to emphasize is that both traverse the prototype chain, so you cannot assume that there is a one-to-one mapping between a matching prototype and the object in question:

var i = 0;

function foo()
{
console.log("foo");
console.log(i++ + ": " + Object.prototype.isPrototypeOf(Object) ) //true
console.log(i++ + ": " + Function.prototype.isPrototypeOf(Function) ) //true

console.log(i++ + ": " + Function.prototype.isPrototypeOf(Function) ) //true
console.log(i++ + ": " + Function.prototype.isPrototypeOf(Object) ) //true

console.log(i++ + ": " + RegExp.prototype.isPrototypeOf( RegExp(/foo/) ) ) //true
console.log(i++ + ": " + Object.prototype.isPrototypeOf( RegExp(/foo/) ) ) //true
console.log(i++ + ": " + Function.prototype.isPrototypeOf( RegExp(/foo/) ) ) //false
console.log(i++ + ": " + Object.prototype.isPrototypeOf(Math) ) //true
console.log(i++ + ": " + Math.isPrototypeOf(Math) ) //false
}

function bar()
{
console.log("bar");
console.log(i++ + ": " + (Object instanceof Object) ) //true

console.log(i++ + ": " + (Function instanceof Function) ) //true
console.log(i++ + ": " + (Function instanceof Object) ) //true

console.log(i++ + ": " + (RegExp(/foo/) instanceof RegExp) ) //true
console.log(i++ + ": " + (RegExp(/foo/) instanceof Object)  ) //true
console.log(i++ + ": " + (RegExp(/foo/) instanceof Function) ) //false
console.log(i++ + ": " + (Math instanceof Object) ) //true
console.log(i++ + ": " + (Math instanceof Math) ) //error
}
try
  {
  foo()
  }
catch(e)
  {
  console.log(JSON.stringify(e));
  }
finally
  {
  try
    {
    bar();
    }
  catch(e)
    {
    console.log(JSON.stringify(e));
    }
  }

References

Paul Sweatte
  • 24,148
  • 7
  • 127
  • 265