How does the performance of instanceof
fair for \"huge libraries\"?
Does it travel up the prototype chain one by one, similar to this?
According to what Felix Kling quoted, all that instanceof does (excluding the error checks) is to check whether the prototype property(which has to be an object) of the Function can be found somewhere down the prototype chain
person instanceof Object
// ROUGHTLY does
return (
person.__proto__==Object.prototype
|| person.__proto__.__proto__==Object.prototype
|| ... );
Here's some pseudocode:
person instanceof Person
//ROUGHTLY equals
person.instanceOf(Person)
person.instanceOf = function(Person) {
if(typeof Person!='object') throw new TypeError;
if(!([[HasInstance]] in Person)) throw new TypeError;
return Person.[[HasInstance]](this /* person */)
}
Person.[[HasInstance]] = function(V) {
if(typeof V!='object') return false;
var O = this.prototype;
if(typeof O!='object') throw new TypeError;
while(true) {
V = V.__proto__; // [[prototype]] (hidden) property
if(V==null) return false;
if(V==O) return true;
}
}
Yeah something like that. Here is the relevant part from the specification:
11.8.6 The instanceof operator
The production RelationalExpression: RelationalExpression instanceof ShiftExpression is evaluated as follows:
- Let lref be the result of evaluating RelationalExpression.
- Let lval be GetValue(lref).
- Let rref be the result of evaluating ShiftExpression.
- Let rval be GetValue(rref).
- If Type(rval) is not Object, throw a TypeError exception.
- If rval does not have a [[HasInstance]] internal method, throw a TypeError exception.
- Return the result of calling the [[HasInstance]] internal method of rval with argument lval.
where calling the [[HasInstance]] method is defined as
15.3.5.3 [[HasInstance]] (V)
Assume F is a Function object.
When the [[HasInstance]] internal method of F is called with value V, the following steps are taken:
- If V is not an object, return false.
- Let O be the result of calling the [[Get]] internal method of F with property name "prototype".
- If Type(O) is not Object, throw a TypeError exception.
- Repeat
a. Let V be the value of the [[Prototype]] internal property of V.
b. If V is null, return false.
c. If O and V refer to the same object, return true.
Regarding performance: This probably depends on the actual implementations in the browsers. There can be huge differences between them so the best thing would be to make some benchmarks, e.g. with http://jsperf.com/.
A problem with instanceof
is that it might not work if you invoke it on elements from different contexts, such as a frame or iframe. For example, let a
be an object you can access via iframe.contentWindow.a
and you want to test whether it is an array, then
iframe.contentWindow.a instanceof Array
will return false
.
in V8 (Chrome's JS engine), there seems to be little-to-no performance hit:
> function A(){}
> function B(){}
> function C(){}
> function D(){}
> B.prototype = new A();
> C.prototype = new B();
> D.prototype = new C();
>
> var objA = new A();
> var objD = new D();
>
> var start = (+new Date()); for(var i=0; i<10000000; i++){ objA instanceof A } console.log((+new Date()) - start);
138
> var start = (+new Date()); for(var i=0; i<10000000; i++){ objD instanceof A } console.log((+new Date()) - start);
138
Firefox shows identical behavior.
Going a bit crazy here, but:
> var classes = [];
> for(var i=0; i<10000; i++){
> classes[i] = function(){};
> i && (classes[i].prototype = new (classes[i-1])());
> }
>
> var obj0 = new classes[0],
> obj9999 = new classes[9999];
>
> var start = (+new Date()); for(var i=0; i<10000000; i++){ obj0 instanceof classes[0] } console.log((+new Date()) - start);
138
> var start = (+new Date()); for(var i=0; i<10000000; i++){ obj999 instanceof classes[0] } console.log((+new Date()) - start);
138
I think it's safe to assume there is no performance hit if it can drill through 10,000 classes and not see 1 ms performance difference :)