What's the difference between isPrototypeOf and instanceof in Javascript?
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)
?
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
Is
a instanceof b
ALWAYS the same asb.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 aTypeError
.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 aTypeError
. -
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 aTypeError
(bound functions don't have aprototype
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 theObject.prototype.isPrototypeOf()
method (indeed the use cases ofinstanceof
are more restricted in thatinstanceof
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 whereObject.prototype
does not lie in the prototype chain ofConstructor.prototype
).
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
- Classes in ECMAScript 6 (final semantics)