Cos'è l'operatore instanceof in JavaScript?


313

La instanceofparola chiave in JavaScript può essere abbastanza confusa quando viene incontrata per la prima volta, poiché le persone tendono a pensare che JavaScript non sia un linguaggio di programmazione orientato agli oggetti.

  • Che cos'è?
  • Quali problemi risolve?
  • Quando è appropriato e quando no?

3
Sebbene le risposte qui sotto siano molto utili, non le usi molto (almeno non lo faccio) in applicazioni di parole reali. Si creerebbe una proprietà object.type che contiene una stringa e la controlla.
Omar Al-Ithawi,

4
JS non ha alcun senso: "foo" instanceof String=> false, 1 instanceof Number=> false, {} instanceof Object=> false. Che cosa?!
Morbusg

12
@morbusg il tuo commento è fuorviante. Il primo "foo" instanceof String => falseè corretto, perché typeof "foo" == 'string'. new String("foo") instanceof String => true, perché typeof String == 'function'- dovresti considerare la funzione come una classe (definizione di classe). Le variabili diventano instanceofalcune function(classe) quando le si assegna come var v = new AnythingWhatTypeofEqualsFunction(). Lo stesso vale per 1. typeof 1 == 'number'- 'numero' non è 'funzione' :) Avanti - {} instanceof Objectè TRUEnel nodo e nei browser moderni
fider

1
@fider: quello era un commento alle specifiche del linguaggio, proveniente da un rubyist.
Morbusg,

@morbusg - ({}) instanceof Objecttornerà true. In effetti il ​​codice che hai scritto ti darà un errore.
DDM,

Risposte:


279

instanceof

L'operando sul lato sinistro (LHS) è l'oggetto reale che viene testato sull'operando sul lato destro (RHS) che è il vero costruttore di una classe. La definizione di base è:

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

Ecco alcuni buoni esempi ed ecco un esempio preso direttamente dal sito degli sviluppatori di 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 degna di nota è la instanceofvalutazione vera se l'oggetto eredita dal prototipo della classe:

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

Questo è p instanceof Personvero poiché peredita da Person.prototype.

Secondo la richiesta del PO

Ho aggiunto un piccolo esempio con un codice di esempio e una spiegazione.

Quando dichiari una variabile, le dai un tipo specifico.

Per esempio:

int i;
float f;
Customer c;

Quanto sopra mostra alcune variabili, vale a dire i,f e c. I tipi sono integer, floate definito dall'utente Customertipo di dati. Tipi come quelli sopra potrebbero essere per qualsiasi lingua, non solo JavaScript. Tuttavia, con JavaScript quando dichiari una variabile non definisci esplicitamente un tipo var x, x potrebbe essere un numero / stringa / un tipo di dati definito dall'utente. Quindi, ciò che instanceoffa è controllare l'oggetto per vedere se è del tipo specificato, quindi dall'alto, prendendo l' Customeroggetto, potremmo fare:

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!

Sopra l'abbiamo visto c stato dichiarato con il tipo Customer. L'abbiamo nuovo e verificato se è di tipo Customero no. Certo che lo è, ritorna vero. Quindi, sempre usando l' Customeroggetto, controlliamo se è un String. No, sicuramente non Stringè un nuovo Customeroggetto non un Stringoggetto. In questo caso, restituisce false.

E 'davvero così semplice!


@Alon - Ho aggiunto un esempio per te. Vedi sopra, color1 è di tipo stringa, quindi quando dici color1 instanceof String;questo restituirà vero perché color1 è una stringa.
JonH

@Alon - Controlla un oggetto per vedere che tipo di oggetto è. Considera un oggetto persona / cliente. Quindi person p = new person()p è ora un tipo di persona e non un tipo di stringa.
JonH

In JavaScript, potrebbe essere difficile capire che una variabile ha la flessibilità di avere un tipo diverso per tutta la sua durata. Esempio di codice: jsfiddle.net/sikusikucom/znSPv
moey

@ Siku-Siku.Com - Non vedo alcun trucco, var zero = 0; alert(zero); zero = "0"; alert(zero)siamo passati da una primitiva inta una primitiva stringsenza problemi.
JonH,

Oh, intendevo dire che era complicato / nuovo capire (non fare ) quella "flessibilità" in JavaScript, esp. per alcuni che sono abituati a un linguaggio che richiede, ad esempio, il cast esplicito per modificare un tipo di variabile. Come hai sottolineato, è molto facile cambiare il tipo, ad esempio da primitivo inta primitivo string.
Moey,

95

C'è un aspetto importante di esempio di ciò che non sembra essere stato trattato in nessuno dei commenti finora: l'eredità. Una variabile in fase di valutazione mediante l'utilizzo di instanceof potrebbe restituire true per più "tipi" a causa dell'ereditarietà prototipale.

Ad esempio, definiamo un tipo e un sottotipo:

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;

Ora che abbiamo un paio di "classi", facciamo alcune istanze e scopriamo di cosa sono istanze:

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

Vedi l'ultima riga? Tutte le "nuove" chiamate a una funzione restituiscono un oggetto che eredita da Object. Questo vale anche quando si utilizza la scorciatoia per la creazione di oggetti:

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

E le definizioni di "classe" stesse? Di cosa sono esempi?

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

Sento che capire che qualsiasi oggetto può essere un'istanza di MULTIPLI tipi è importante, dal momento che il mio (erroneamente) presumi che potresti differenziare, dire e oggetto e una funzione usando instanceof. Come quest'ultimo esempio mostra chiaramente una funzione è un oggetto.

Ciò è importante anche se si utilizzano modelli di ereditarietà e si desidera confermare la discendenza di un oggetto con metodi diversi dalla tipizzazione anatra.

Spero che aiuti chiunque a esplorare instanceof.


Ancora più straordinario è che dopo aver ereditato dal prototipo con SubFoo.prototype = new Foo();te puoi aggiungere altri metodi e il subfoo instanceof Foocontrollo continuerà a passaresubfoo instanceof SubFoo
Cory Danielson,

87

Le altre risposte qui sono corrette, ma non riescono a capire come instanceoffunziona effettivamente, il che potrebbe essere di interesse per alcuni avvocati di lingua là fuori.

Ogni oggetto in JavaScript ha un prototipo, accessibile attraverso la __proto__proprietà. Le funzioni hanno anche una prototypeproprietà, che è l'iniziale __proto__per qualsiasi oggetto creato da loro. Quando viene creata una funzione, viene assegnato un oggetto unico per prototype. L' instanceofoperatore utilizza questa unicità per darti una risposta. Ecco come instanceofpotrebbe apparire se lo scrivessi come una funzione.

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__;
  }
}

Questo è sostanzialmente parafrasando ECMA-262 edizione 5.1 (noto anche come ES5), sezione 15.3.5.3.

Nota che puoi riassegnare qualsiasi oggetto alla prototypeproprietà di una funzione e puoi riassegnare la __proto__proprietà di un oggetto dopo che è stata costruita. Questo ti darà alcuni risultati interessanti:

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

5
Vale la pena notare che la manipolazione diretta della __proto__proprietà " " non è consentita in IE. Se ricordo bene, la manipolazione diretta della proprietà non è inclusa nelle specifiche ECMA, quindi è probabilmente una cattiva idea usarla per incarichi diversi da quelli accademici.
webnesto,

@webnesto, è vero, proto non è nelle specifiche. Non sapevo che IE non lo supportasse però. Quando dici manipolazione diretta, vuoi dire che non è affatto esposto al codice JS o semplicemente non è scrivibile?
Jay Conrod,

2
Non sicuro al 100% sulle versioni precedenti. Sembra da qui ( stackoverflow.com/questions/8413505/proto-for-ie9-or-ie10 ) che in IE9 è almeno leggibile (anche se non modificabile). È anche degno di nota il fatto che sembra che i browser lo stiano completamente abbandonando.
webnesto,

4
Comprendere l'esistenza implicita della proprietà proto è importante sia che sia accessibile al codice utente o meno. +10 se potessi citare le specifiche, questo è esattamente quello che sono venuto qui a cercare.
Mike Edwards,

3
Per ottenere il collegamento prototipo, utilizzare Object.getPrototypeOf(o), sarà lo stesso __proto__descritto, ma conforme a ECMAScript.
froginvasion,

45

Penso che valga la pena notare che instanceof è definito dall'uso della "nuova" parola chiave quando si dichiara l'oggetto. Nell'esempio di 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)

Ciò che non ha menzionato è questo;

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

Specificando "new" in realtà è stato copiato lo stato finale della funzione di costruzione String in color1 var, anziché impostarlo semplicemente sul valore restituito. Penso che questo mostri meglio cosa fa la nuova parola chiave;

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'

L'uso di "nuovo" assegna il valore di "this" all'interno della funzione al var dichiarato, mentre se non lo si utilizza si assegna invece il valore di ritorno.


2
Non ha senso utilizzare newcon nessuno dei tipi di JavaScript che rende la risposta accettata molto più confusa per i principianti. text = String('test')e options = {}non verranno testati da instanceofma, piuttosto, con typeofinvece.
Ryan,

3
Date.getTime () // fail. sì, il nuovo è importante.
Stephen Belanger,

1
Prova a eseguirlo in una console. Verrà visualizzato un errore perché getTime () non esiste. È necessario utilizzare nuovo.
Stephen Belanger,

8

E puoi usarlo per la gestione degli errori e il debug, in questo modo:

try{
    somefunction();
} 
catch(error){
    if (error instanceof TypeError) {
        // Handle type Error
    } else if (error instanceof ReferenceError) {
        // Handle ReferenceError
    } else {
        // Handle all other error types
    }
}

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);

3

Che cos'è?

Javascript è un linguaggio prototipo, il che significa che utilizza i prototipi per l'ereditarietà. l' instanceofoperatore verifica se la prototypeproprietà di una funzione di costruzione è presente nella __proto__catena di un oggetto. Ciò significa che eseguirà le seguenti operazioni (presupponendo che testObj sia un oggetto funzione):

obj instanceof testObj;
  1. Controlla se il prototipo dell'oggetto è uguale al prototipo del costruttore: obj.__proto__ === testObj.prototype >> se questo true instanceofsarà restituito true.
  2. Salirà la catena del prototipo. Ad esempio: obj.__proto__.__proto__ === testObj.prototype >> se questo è true instanceofrestituitotrue .
  3. Ripeterà il passaggio 2 fino a quando non verrà ispezionato il prototipo completo dell'oggetto. Se nessuna parte sulla catena di prototipi dell'oggetto è abbinato con testObj.prototypeallora instanceofoperatore tornerà false.

Esempio:

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

Quali problemi risolve?

Risolve il problema di controllare convenientemente se un oggetto deriva da un certo prototipo. Ad esempio, quando una funzione riceve un oggetto che può avere vari prototipi. Quindi, prima di utilizzare i metodi della catena di prototipi, possiamo usare l' instanceofoperatore per verificare se questi metodi sono sull'oggetto.

Esempio:

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);

Qui nella talk()funzione viene prima verificato se il prototipo si trova sull'oggetto. Successivamente, viene scelto il metodo appropriato da eseguire. Non eseguire questo controllo potrebbe comportare l'esecuzione di un metodo che non esiste e quindi un errore di riferimento.

Quando è appropriato e quando no?

Abbiamo già analizzato questo. Usalo quando hai bisogno di controllare il prototipo di un oggetto prima di fare qualcosa con esso.


1
Penso che tu volessi scrivere qualcosa di diverso da " il prototipo di una funzione di costruzione appare da qualche parte nella proprietà prototipo di un costruttore "
Bergi,

Potresti voler dire che sarebbe più appropriato che le persone condividessero un'interfaccia e nominassero entrambi quei metodi PersonX.prototype.talk, in modo che la talkfunzione potesse semplicemente fare person.talk().
Bergi,

Hai ragionevolmente aggiornato, quindi la definizione è migliore. Grazie per la segnalazione!
Willem van der Veen,

Inoltre, non utilizzare __proto__nella documentazione, è obsoleto - scrivi Object.getPrototype()invece
Bergi

1

Alla domanda "Quando è appropriato e quando no?", I miei 2 centesimi:

instanceofè raramente utile nel codice di produzione, ma utile nei test in cui si desidera affermare che il codice restituisce / crea oggetti dei tipi corretti. Essendo esplicito sui tipi di oggetti restituiti / creati dal codice, i test diventano più potenti come strumento per comprendere e documentare il codice.


1

instanceofè solo zucchero sintattico per 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 dipende solo dal prototipo di un costruttore di un oggetto.

Un costruttore è solo una normale funzione. A rigor di termini è un oggetto funzione, dal momento che tutto è un oggetto in Javascript. E questo oggetto funzione ha un prototipo, perché ogni funzione ha un prototipo.

Un prototipo è solo un oggetto normale, che si trova all'interno della catena prototipo di un altro oggetto. Ciò significa che essere nella catena di prototipi di un altro oggetto fa un oggetto a 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

L' instanceofoperatore dovrebbe essere evitato perché simula le classi, che non esistono in Javascript. Nonostante la classparola chiave non sia in ES2015, poiché classè di nuovo solo zucchero sintattico per ... ma questa è un'altra storia.


1
La prima riga non è corretta! Per ulteriori informazioni, vedere qui : "isPrototypeOf () differisce dall'operatore instanceof. Nell'espressione" object instanceof di AFunction ", la catena di prototipi di oggetti viene verificata rispetto a AFunction.protototype, non rispetto a AFunction stesso."
Yan Foto

1
@Yan E ancora instanceofè derivato isPrototypeOf. Io chiamo questo zucchero sintattico. E la tua fonte MDN è uno scherzo, vero?

No. Non è e non doveva essere uno scherzo, ma è ancora il collegamento sbagliato. Mi è stato destinato ad avere questo postato. Non voglio entrare nei tecnicismi, ma instanceof non faccio la stessa cosa di isPrototypeOf. Questo è tutto.
Yan Foto

0

Ho appena trovato un'applicazione reale e la userò più spesso ora, credo.

Se si utilizzano eventi jQuery, a volte si desidera scrivere una funzione più generica che può anche essere chiamata direttamente (senza evento). È possibile utilizzare instanceofper verificare se il primo parametro della funzione è un'istanza di jQuery.Evente reagire in modo appropriato.

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

Nel mio caso, la funzione doveva calcolare qualcosa per tutti (tramite evento click su un pulsante) o solo un elemento specifico. Il codice che ho usato:

var recalc = function (el) { 
    el = (el == undefined || el instanceof $.Event) ? $('span.allItems') : $(el);
    // calculate...
};
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.