2011-05-08 8 views
9

¿Cómo es el rendimiento de instanceof justo para "grandes bibliotecas"?¿Cómo funciona exactamente Javascript instanceof? ¿Es un estilo lento?

¿Viaja por la cadena prototipo uno por uno, similar a esto? :

//.. 
var _ = john.constructor; 
while (true) { 
    if (_ === Human) { 
     return true; 
    } 
    _ = _.prototype.constructor 
} 
return false; 
//.. 

Es instanceof relativamente unperfomant a continuación, en comparación con el almacenamiento de un número de identificación único interfaz en la propiedad de cada objeto.

+0

relacionadas: https://stackoverflow.com/a/45656957/632951 – Pacerier

Respuesta

11

Sí, algo así. Aquí está la parte pertinente de la specification:

11.8.6 The instanceof operator

La producción RelationalExpression: RelationalExpressioninstanceofShiftExpression se evalúa de la siguiente manera:

  1. Deje Lref ser el resultado de evaluar RelationalExpression.
  2. Deje que lval sea GetValue (lref).
  3. Deje rref Sea el resultado de la evaluación de ShiftExpression.
  4. Deje que rval sea GetValue (rref).
  5. Si Tipo (rval) no es un objeto, ejecute TypeError excepción.
  6. Si rval no tiene un método interno [[HasInstance]], lanzar una TypeError excepción.
  7. Devuelve el resultado de llamar al método interno [[HasInstance]] de rval con el argumento lval.

donde llamando a la [[HasInstance]] método se define como

15.3.5.3 [[HasInstance]] (V)

Supongamos F es un objeto Function.

Cuando el método interno [[HasInstance]] de F se llama con valor V, se toman los pasos siguientes:

  1. Si V no es un objeto, de regreso falsa .
  2. Vamos O ser el resultado de una llamada al método interno [[Obtener]] de F con nombre de la propiedad "prototipo".
  3. Si Tipo (O) no es un objeto, ejecute TypeError excepción.
  4. Repita
    a. Deje que V sea el valor de la propiedad interna [[Prototype]] de V.
    b. Si V es nulo, devuelve falso.
    c. Si O y V se refieren al mismo objeto, devuelva verdadero.

En cuanto al rendimiento: Esto probablemente depende de las implementaciones reales en los navegadores. Puede haber grandes diferencias entre ellos, así que lo mejor sería hacer algunos puntos de referencia, p. con http://jsperf.com/.


Un problema con instanceof es que no podría funcionar si se invoca en elementos de diferentes contextos, como un marco o iframe. Por ejemplo, supongamos a ser un objeto se puede acceder a través de iframe.contentWindow.a y que desea probar si se trata de una matriz, entonces

iframe.contentWindow.a instanceof Array 

regresará false.

+0

Eso no es un problema, es una gran ventaja! :) ¡Puedes jugar con 'iframe.contentwindow.Array.prototype' y luego tener arreglos" especiales "! O '.Object.prototype' :) –

+0

@cwolves: que podría tener un comportamiento inesperado en el iframe. Es un problema si uno no sabe sobre eso. –

11

en V8 (motor de JS de Chrome), parece que hay poco o ningún impacto en el rendimiento:

> 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 muestra un comportamiento idéntico.

Yendo un poco loco aquí, pero:

> 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 

Creo que es seguro asumir que no hay impacto en el rendimiento si se puede perforar a través de 10.000 clases y no ver 1 ms diferencia de rendimiento :)

+1

volviéndose un poco loco es exactamente lo que se requiere a veces – Flion

7

Según lo que cita Félix Kling, toda esa instancia de does (excluyendo las comprobaciones de error) es verificar si la propiedad prototipo (que debe ser un objeto) de la función puede encontrarse en alguna parte de la cadena prototipo

person instanceof Object 
// ROUGHTLY does 
return (
    person.__proto__==Object.prototype 
    || person.__proto__.__proto__==Object.prototype 
    || ...); 

aquí hay algo de pseudocódigo:

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; 
    } 
} 
Cuestiones relacionadas