¿Cuál es el operador instanceof en JavaScript?

313

La instanceofpalabra clave en JavaScript puede ser bastante confusa cuando se encuentra por primera vez, ya que las personas tienden a pensar que JavaScript no es un lenguaje de programación orientado a objetos.

  • ¿Qué es?
  • ¿Qué problemas resuelve?
  • ¿Cuándo es apropiado y cuándo no?
Alon Gubkin
fuente
3
Aunque las respuestas a continuación son muy útiles, no lo usa mucho (al menos yo no) en aplicaciones de palabras reales. Uno haría una propiedad object.type que contiene una cadena y la verifica.
Omar Al-Ithawi
44
JS no tiene ningún sentido: "foo" instanceof String=> falso, 1 instanceof Number=> falso, {} instanceof Object=> falso. ¡¿Que qué?!
morbusg
12
@morbusg tu comentario es engañoso. Primero "foo" instanceof String => falsees correcto, porque typeof "foo" == 'string'. new String("foo") instanceof String => true, porque typeof String == 'function'- debe tratar la función como clase (definición de clase). La variable se convierte en instanceofalguna function(clase) cuando la asigna como var v = new AnythingWhatTypeofEqualsFunction(). Lo mismo se aplica a 1. typeof 1 == 'number'- 'número' no es 'función' :) Siguiente - {} instanceof Objectestá TRUEen nodos y navegadores modernos
fider
1
@fider: Eso fue un comentario sobre la especificación del lenguaje, proveniente de un rubí.
morbusg
@morbusg: ({}) instanceof Objectvolverá true. De hecho, el código que escribió le dará un error.
DDM

Respuestas:

279

en vez de

El operando del lado izquierdo (LHS) es el objeto real que se prueba con el operando del lado derecho (RHS) que es el constructor real de una clase. La definición básica es:

Checks the current object and returns true if the object
is of the specified object type.

Aquí hay algunos buenos ejemplos y aquí hay un ejemplo tomado directamente del sitio de desarrolladores de Mozilla :

var color1 = new String("green");
color1 instanceof String; // returns true
var color2 = "coral"; //no type specified
color2 instanceof String; // returns false (color2 is not a String object)

Una cosa que vale la pena mencionar es que se instanceofevalúa como verdadero si el objeto hereda del prototipo de la clase:

var p = new Person("Jon");
p instanceof Person

Eso es p instanceof Personcierto ya que phereda de Person.prototype.

Por solicitud del OP

He agregado un pequeño ejemplo con un código de muestra y una explicación.

Cuando declaras una variable, le das un tipo específico.

Por ejemplo:

int i;
float f;
Customer c;

Lo anterior se muestran algunas variables, a saber i, fy c. Los tipos son integer, floaty una definida por el usuario Customertipo de datos. Tipos como el anterior podrían ser para cualquier idioma, no solo JavaScript. Sin embargo, con JavaScript cuando declara una variable que no define explícitamente un tipo var x, x podría ser un número / cadena / un tipo de datos definido por el usuario. Entonces, lo que instanceofhace es verificar el objeto para ver si es del tipo especificado, por lo que desde arriba Customertomamos el objeto que podríamos hacer:

var c = new Customer();
c instanceof Customer; //Returns true as c is just a customer
c instanceof String; //Returns false as c is not a string, it's a customer silly!

Arriba hemos visto que cse declaró con el tipo Customer. Lo hemos renovado y comprobado si es de tipo Customero no. Claro que es cierto. Luego, aún usando el Customerobjeto, verificamos si es a String. No, definitivamente no Stringhemos renovado un Customerobjeto, no un Stringobjeto. En este caso, devuelve falso.

¡Es realmente así de simple!

JonH
fuente
@Alon: agregué un ejemplo para ti. Ver arriba, color1 es de tipo cadena, por lo que cuando diga color1 instanceof String;esto devolverá verdadero porque color1 es una cadena.
JonH
@Alon: comprueba un objeto para ver qué tipo de objeto es. Considere una persona / objeto de cliente. Entonces person p = new person()p es ahora un tipo de persona y no un tipo de cadena.
JonH
En JavaScript, puede ser complicado entender que una variable tiene la flexibilidad de tener un tipo diferente a lo largo de su vida útil. Ejemplo de código: jsfiddle.net/sikusikucom/znSPv
moey
@ Siku-Siku.Com - No veo ningún truco, var zero = 0; alert(zero); zero = "0"; alert(zero)pasamos de primitivo inta primitivo stringsin ningún problema.
JonH
Oh, quise decir que era complicado / nuevo entender (no hacer ) esa "flexibilidad" en JavaScript, especialmente. para algunos que están acostumbrados a un lenguaje que requiere, por ejemplo, una conversión explícita para alterar un tipo de variable. Como señaló, es muy fácil cambiar el tipo de, por ejemplo, primitivo inta primitivo string.
Moey
95

Hay una faceta importante de la instancia que no parece estar cubierta en ninguno de los comentarios hasta el momento: la herencia. Una variable que se evalúa mediante el uso de instanceof podría ser verdadera para múltiples "tipos" debido a la herencia prototípica.

Por ejemplo, definamos un tipo y un subtipo:

function Foo(){ //a Foo constructor
    //assign some props
    return this;
}

function SubFoo(){ //a SubFoo constructor
    Foo.call( this ); //inherit static props
    //assign some new props
    return this;
}

SubFoo.prototype = Object.create(Foo.prototype); // Inherit prototype
SubFoo.prototype.constructor = SubFoo;

Ahora que tenemos un par de "clases", hagamos algunas instancias y descubramos de qué son instancias:

var 
    foo = new Foo()
,   subfoo = new SubFoo()
;

alert( 
    "Q: Is foo an instance of Foo? "
+   "A: " + ( foo instanceof Foo ) 
); // -> true

alert( 
    "Q: Is foo an instance of SubFoo? " 
+   "A: " + ( foo instanceof SubFoo ) 
); // -> false

alert( 
    "Q: Is subfoo an instance of Foo? "
+   "A: " + ( subfoo instanceof Foo ) 
); // -> true

alert( 
    "Q: Is subfoo an instance of SubFoo? "
+   "A: " + ( subfoo instanceof SubFoo ) 
); // -> true

alert( 
    "Q: Is subfoo an instance of Object? "
+   "A: " + ( subfoo instanceof Object ) 
); // -> true

¿Ves esa última línea? Todas las llamadas "nuevas" a una función devuelven un objeto que hereda de Object. Esto es válido incluso cuando se usa la creación abreviada de creación de objetos:

alert( 
    "Q: Is {} an instance of Object? "
+   "A: " + ( {} instanceof Object ) 
); // -> true

¿Y qué hay de las definiciones de "clase" en sí mismas? ¿De qué son instancias?

alert( 
    "Q: Is Foo an instance of Object? "
+   "A:" + ( Foo instanceof Object) 
); // -> true

alert( 
    "Q: Is Foo an instance of Function? "
+   "A:" + ( Foo instanceof Function) 
); // -> true

Creo que es importante comprender que cualquier objeto puede ser una instancia de MÚLTIPLES tipos, ya que usted (incorrectamente) supone que puede diferenciar entre, digamos y un objeto y una función mediante el uso de instanceof. Como este último ejemplo muestra claramente que una función es un objeto.

Esto también es importante si está utilizando algún patrón de herencia y desea confirmar la progenie de un objeto mediante métodos distintos al tipeo de pato.

Espero que ayude a cualquiera a explorar instanceof.

webnesto
fuente
Aún más sorprendente es que después de heredar del prototipo con SubFoo.prototype = new Foo();él, puede agregarle más métodos, y la subfoo instanceof Fooverificación aún pasará tan bien comosubfoo instanceof SubFoo
Cory Danielson
87

Las otras respuestas aquí son correctas, pero no entienden cómo instanceoffunciona realmente, lo que puede ser de interés para algunos abogados de idiomas.

Cada objeto en JavaScript tiene un prototipo, accesible a través de la __proto__propiedad. Las funciones también tienen una prototypepropiedad, que es la inicial __proto__de cualquier objeto creado por ellas. Cuando se crea una función, se le da un objeto único para prototype. El instanceofoperador utiliza esta singularidad para darle una respuesta. Así instanceofse vería si lo escribiera como una función.

function instance_of(V, F) {
  var O = F.prototype;
  V = V.__proto__;
  while (true) {
    if (V === null)
      return false;
    if (O === V)
      return true;
    V = V.__proto__;
  }
}

Esto es básicamente parafraseando la edición 5.1 de ECMA-262 (también conocida como ES5), sección 15.3.5.3.

Tenga en cuenta que puede reasignar cualquier objeto a la prototypepropiedad de una función , y puede reasignar la __proto__propiedad de un objeto después de su construcción. Esto te dará algunos resultados interesantes:

function F() { }
function G() { }
var p = {};
F.prototype = p;
G.prototype = p;
var f = new F();
var g = new G();

f instanceof F;   // returns true
f instanceof G;   // returns true
g instanceof F;   // returns true
g instanceof G;   // returns true

F.prototype = {};
f instanceof F;   // returns false
g.__proto__ = {};
g instanceof G;   // returns false
Jay Conrod
fuente
55
Vale la pena señalar que la manipulación directa de la __proto__propiedad " " no está permitida en IE. Si recuerdo correctamente, la manipulación directa de la propiedad tampoco está incluida en la especificación de ECMA, por lo que probablemente sea una mala idea usarla para tareas que no sean actividades académicas.
webnesto
@webnesto, eso es cierto, proto no está en la especificación. Sin embargo, no sabía que IE no lo soportaba. Cuando dice manipulación directa, ¿quiere decir que no está expuesto al código JS en absoluto, o simplemente no se puede escribir?
Jay Conrod
2
No estoy 100% seguro en versiones anteriores. Parece que desde aquí ( stackoverflow.com/questions/8413505/proto-for-ie9-or-ie10 ) que en IE9 es al menos legible (aunque no mutable). También cabe destacar que parece que los navegadores pueden estar cayéndolo por completo.
webnesto
44
Comprender la existencia implícita de la propiedad proto es importante tanto si es accesible para el código de usuario como si no. +10 si pudiera por citar las especificaciones, esto es exactamente lo que vine a buscar aquí.
Mike Edwards
3
Para obtener el enlace del prototipo, use Object.getPrototypeOf(o), será el mismo __proto__que describe, pero se ajusta a ECMAScript.
froginvasion
45

Creo que vale la pena señalar que la instancia de se define mediante el uso de la palabra clave "nueva" al declarar el objeto. En el ejemplo de JonH;

var color1 = new String("green");
color1 instanceof String; // returns true
var color2 = "coral";
color2 instanceof String; // returns false (color2 is not a String object)

Lo que no mencionó es esto;

var color1 = String("green");
color1 instanceof String; // returns false

Especificar "nuevo" en realidad copió el estado final de la función del constructor de cadenas en el color1 var, en lugar de simplemente establecerlo en el valor de retorno. Creo que esto muestra mejor lo que hace la nueva palabra clave;

function Test(name){
    this.test = function(){
        return 'This will only work through the "new" keyword.';
    }
    return name;
}

var test = new Test('test');
test.test(); // returns 'This will only work through the "new" keyword.'
test // returns the instance object of the Test() function.

var test = Test('test');
test.test(); // throws TypeError: Object #<Test> has no method 'test'
test // returns 'test'

El uso de "new" asigna el valor de "this" dentro de la función a la var declarada, mientras que no usarlo asigna el valor de retorno en su lugar.

Stephen Belanger
fuente
2
No tiene sentido usarlo newcon ninguno de los tipos de JavaScript, lo que hace que la respuesta aceptada sea mucho más confusa para los principiantes. text = String('test')y options = {}no vamos a probarlo instanceof, sino más bien con typeof.
Ryan
3
Date.getTime () // fail. Sí, lo nuevo es importante.
Stephen Belanger
1
Intenta ejecutar eso en una consola. Obtendrá un error porque getTime () no existe. Necesitas usar nuevo.
Stephen Belanger
8

Y puede usarlo para el manejo de errores y la depuración, como este:

try{
    somefunction();
} 
catch(error){
    if (error instanceof TypeError) {
        // Handle type Error
    } else if (error instanceof ReferenceError) {
        // Handle ReferenceError
    } else {
        // Handle all other error types
    }
}
Tarek Saied
fuente
3
//Vehicle is a function. But by naming conventions
//(first letter is uppercase), it is also an object
//constructor function ("class").
function Vehicle(numWheels) {
    this.numWheels = numWheels;
}

//We can create new instances and check their types.
myRoadster = new Vehicle(4);
alert(myRoadster instanceof Vehicle);
Yfeldblum
fuente
3

¿Qué es?

Javascript es un lenguaje prototípico, lo que significa que utiliza prototipos para "herencia". el instanceofoperador prueba si el tipo de prototypepropiedad de una función constructora está presente en la __proto__cadena de un objeto. Esto significa que hará lo siguiente (suponiendo que testObj es un objeto de función):

obj instanceof testObj;
  1. Verifique si el prototipo del objeto es igual al prototipo del constructor: obj.__proto__ === testObj.prototype >> si esto true instanceofregresará true.
  2. Subirá por la cadena prototipo. Por ejemplo: obj.__proto__.__proto__ === testObj.prototype >> si esto es true instanceofregresará true.
  3. Repetirá el paso 2 hasta que se inspeccione el prototipo completo del objeto. Si ninguna parte en la cadena de prototipo del objeto se corresponde con la testObj.prototypecontinuación instanceofdel operador regresará false.

Ejemplo:

function Person(name) {
  this.name = name;
}
var me = new Person('Willem');

console.log(me instanceof Person); // true
// because:  me.__proto__ === Person.prototype  // evaluates true

console.log(me instanceof Object); // true
// because:  me.__proto__.__proto__ === Object.prototype  // evaluates true

console.log(me instanceof Array);  // false
// because: Array is nowhere on the prototype chain

¿Qué problemas resuelve?

Solucionó el problema de verificar convenientemente si un objeto deriva de un cierto prototipo. Por ejemplo, cuando una función recibe un objeto que puede tener varios prototipos. Luego, antes de usar métodos de la cadena de prototipos, podemos usar el instanceofoperador para verificar si estos métodos están en el objeto.

Ejemplo:

function Person1 (name) {
  this.name = name;
}

function Person2 (name) {
  this.name = name;
}

Person1.prototype.talkP1 = function () {
  console.log('Person 1 talking');
}

Person2.prototype.talkP2 = function () {
  console.log('Person 2 talking');
}


function talk (person) {
  if (person instanceof Person1) {
    person.talkP1();
  }
  
  if (person instanceof Person2) {
    person.talkP2();
  }
  
  
}

const pers1 = new Person1 ('p1');
const pers2 = new Person2 ('p2');

talk(pers1);
talk(pers2);

Aquí en la talk()función primero se verifica si el prototipo se encuentra en el objeto. Después de esto, se elige el método apropiado para ejecutar. Si no se realiza esta comprobación, se podría ejecutar un método que no existe y, por lo tanto, un error de referencia.

¿Cuándo es apropiado y cuándo no?

Ya hemos repasado esto. Úselo cuando necesite verificar el prototipo de un objeto antes de hacer algo con él.

Willem van der Veen
fuente
1
Creo que quería escribir algo más que " el prototipo de una función de constructor aparece en algún lugar de la propiedad prototipo de un constructor "
Bergi
Es posible que desee mencionar que sería más apropiado que las personas compartan una interfaz y nombren ambos métodos PersonX.prototype.talk, de modo que la talkfunción pueda simplemente funcionar person.talk().
Bergi
Estabas completamente actualizado, así que la definición es mejor. Gracias por señalar!
Willem van der Veen
Además, no lo use __proto__en la documentación, está en desuso - escriba en su Object.getPrototype()lugar
Bergi el
1

Sobre la pregunta "¿Cuándo es apropiado y cuándo no?", Mis 2 centavos:

instanceofrara vez es útil en el código de producción, pero es útil en las pruebas en las que desea afirmar que su código devuelve / crea objetos del tipo correcto. Al ser explícito sobre los tipos de objetos que devuelve / crea su código, sus pruebas se vuelven más poderosas como una herramienta para comprender y documentar su código.

Andrew Magee
fuente
1

instanceofes solo azúcar sintáctico para isPrototypeOf:

function Ctor() {}
var o = new Ctor();

o instanceof Ctor; // true
Ctor.prototype.isPrototypeOf(o); // true

o instanceof Ctor === Ctor.prototype.isPrototypeOf(o); // equivalent

instanceof solo depende del prototipo de un constructor de un objeto.

Un constructor es solo una función normal. Estrictamente hablando, es un objeto de función, ya que todo es un objeto en Javascript. Y este objeto de función tiene un prototipo, porque cada función tiene un prototipo.

Un prototipo es solo un objeto normal, que se encuentra dentro de la cadena de prototipo de otro objeto. Eso significa que estar en la cadena de prototipo de otro objeto convierte un objeto en un prototipo:

function f() {} //  ordinary function
var o = {}, // ordinary object
 p;

f.prototype = o; // oops, o is a prototype now
p = new f(); // oops, f is a constructor now

o.isPrototypeOf(p); // true
p instanceof f; // true

El instanceofoperador debe evitarse porque falsifica clases, que no existen en Javascript. A pesar de que la classpalabra clave tampoco está en ES2015, ya classque nuevamente es solo azúcar sintáctica para ... pero esa es otra historia.


fuente
1
¡La primera línea es incorrecta! Consulte aquí para obtener más información: "isPrototypeOf () difiere del operador de instancia de. En la expresión" instancia de objeto de función AF ", la cadena de prototipo de objeto se verifica contra AFunction.prototype, no contra AFunction en sí".
Yan Foto
1
@Yan Y aún instanceofse deriva de isPrototypeOf. Yo llamo a esto azúcar sintáctico. Y tu fuente de MDN es una broma, ¿no?
No. No es ni se suponía que fuera una broma, pero sigue siendo el enlace equivocado. Estaba destinado a tener éste registró. No quiero entrar en tecnicismos, pero instanceof no hace lo mismo que isPrototypeOf. Eso es.
Yan Foto
0

Acabo de encontrar una aplicación del mundo real y la usaré con más frecuencia ahora, creo.

Si usa eventos jQuery, a veces desea escribir una función más genérica que también se puede llamar directamente (sin evento). Puede usar instanceofpara verificar si el primer parámetro de su función es una instancia de jQuery.Eventy reacciona adecuadamente.

var myFunction = function (el) {                
    if (el instanceof $.Event) 
        // event specific code
    else
        // generic code
};

$('button').click(recalc);    // Will execute event specific code
recalc('myParameter');  // Will execute generic code

En mi caso, la función necesitaba calcular algo para todos (mediante un evento de clic en un botón) o solo un elemento específico. El código que usé:

var recalc = function (el) { 
    el = (el == undefined || el instanceof $.Event) ? $('span.allItems') : $(el);
    // calculate...
};
jussty
fuente