Was ist die Instanz des Operators in JavaScript?

313

Das instanceofSchlüsselwort in JavaScript kann beim ersten Auftreten ziemlich verwirrend sein, da die Leute glauben, dass JavaScript keine objektorientierte Programmiersprache ist.

  • Was ist es?
  • Welche Probleme löst es?
  • Wann ist es angebracht und wann nicht?
Alon Gubkin
quelle
3
Obwohl die folgenden Antworten sehr nützlich sind, verwenden Sie sie in realen Wortanwendungen nicht oft (zumindest nicht). Man würde eine object.type-Eigenschaft erstellen, die eine Zeichenfolge enthält und diese überprüft.
Omar Al-Ithawi
4
JS macht überhaupt keinen Sinn: "foo" instanceof String=> falsch, 1 instanceof Number=> falsch, {} instanceof Object=> falsch. Sag was?!
Morbusg
12
@morbusg Ihr Kommentar ist irreführend. Erstens "foo" instanceof String => falseist richtig, weil typeof "foo" == 'string'. new String("foo") instanceof String => true, weil typeof String == 'function'- Sie sollten die Funktion wie eine Klasse behandeln (Definition der Klasse). Variable wird zu instanceofeiner function(Klasse), wenn Sie sie als zuweisen var v = new AnythingWhatTypeofEqualsFunction(). Gleiches gilt für 1. typeof 1 == 'number'- 'Nummer' ist nicht 'Funktion' :) Weiter - {} instanceof Objectist TRUEin Knoten und modernen Browsern
Fider
1
@fider: Das war ein Kommentar zur Sprachspezifikation, der von einem Rubinisten stammt.
Morbusg
@morbusg - ({}) instanceof Objectwird zurückkehren true. Tatsächlich gibt Ihnen der von Ihnen geschriebene Code einen Fehler.
DDM

Antworten:

279

Instanz von

Der LHS-Operand (Left Hand Side) ist das tatsächliche Objekt, das mit dem RHS-Operanden (Right Hand Side) getestet wird, der der eigentliche Konstruktor einer Klasse ist. Die grundlegende Definition lautet:

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

Hier sind einige gute Beispiele und hier ein Beispiel, das direkt von Mozillas Entwicklerseite stammt :

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)

Eine erwähnenswerte Sache ist die instanceofBewertung als wahr, wenn das Objekt vom Prototyp der Klasse erbt:

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

Das ist p instanceof Personwahr, da perbt von Person.prototype.

Auf Anfrage des OP

Ich habe ein kleines Beispiel mit Beispielcode und einer Erklärung hinzugefügt.

Wenn Sie eine Variable deklarieren, geben Sie ihr einen bestimmten Typ.

Zum Beispiel:

int i;
float f;
Customer c;

Die oben zeigen Ihnen einige Variablen, nämlich i, fund c. Die Typen sind integer, floatund eine Benutzer - definierte CustomerDatentypen. Typen wie die oben genannten können für jede Sprache verwendet werden, nicht nur für JavaScript. Wenn Sie jedoch mit JavaScript eine Variable deklarieren, definieren Sie keinen Typ explizit. var xX kann eine Zahl / Zeichenfolge / ein benutzerdefinierter Datentyp sein. Also , was instanceoftut , ist es das Objekt überprüft, ob es von der Art ist , angegeben , so von oben die Einnahme CustomerObjekt , das wir tun könnten:

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!

Oben haben wir gesehen, dass dies cmit dem Typ deklariert wurde Customer. Wir haben es neu gemacht und geprüft, ob es vom Typ ist Customeroder nicht. Sicher ist, es gibt wahr zurück. Dann Customerprüfen wir immer noch mit dem Objekt, ob es ein ist String. Nein, definitiv nicht Stringwir haben ein CustomerObjekt neu gemacht, kein StringObjekt. In diesem Fall wird false zurückgegeben.

So einfach ist das wirklich!

JonH
quelle
@Alon - Ich habe ein Beispiel für dich hinzugefügt. Siehe oben, color1 ist vom Typ string. Wenn Sie also sagen, dass color1 instanceof String;dies true zurückgibt, weil color1 ein String ist.
JonH
@Alon - Überprüft ein Objekt, um festzustellen, um welchen Objekttyp es sich handelt. Betrachten Sie eine Person / ein Kundenobjekt. Also ist person p = new person()p jetzt ein Personentyp und kein Zeichenfolgentyp.
JonH
In JavaScript kann es schwierig sein zu verstehen, dass eine Variable die Flexibilität hat, während ihrer gesamten Lebensdauer einen anderen Typ zu haben. Codebeispiel
moey
@ Siku-Siku.Com - Ich sehe keinen Trick, var zero = 0; alert(zero); zero = "0"; alert(zero)wir sind ohne Probleme von einem Primitiv intzu einem Primitiv übergegangen string.
JonH
Oh, ich meinte, dass es schwierig / neu war, diese "Flexibilität" in JavaScript zu verstehen (nicht zu tun ), insb. für einige, die an eine Sprache gewöhnt sind, die z. B. explizites Casting erfordert, um einen Variablentyp zu ändern. Wie Sie bereits betont haben, ist es sehr einfach, den Typ von z . B. primitiv intin primitiv zu ändern string.
Moey
95

Es gibt eine wichtige Facette, die in keinem der bisherigen Kommentare behandelt zu werden scheint: Vererbung. Eine Variable, die mithilfe von instanceof ausgewertet wird, kann aufgrund der prototypischen Vererbung für mehrere "Typen" true zurückgeben.

Definieren wir zum Beispiel einen Typ und einen Subtyp:

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;

Nachdem wir nun ein paar "Klassen" haben, können wir einige Instanzen erstellen und herausfinden, wofür sie Instanzen sind:

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

Sehen Sie diese letzte Zeile? Alle "neuen" Aufrufe einer Funktion geben ein Objekt zurück, das von Object erbt. Dies gilt auch bei Verwendung der Kurzform zur Objekterstellung:

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

Und was ist mit den "Klassen" -Definitionen selbst? Wovon sind sie Beispiele?

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

Ich bin der Meinung, dass es wichtig ist zu verstehen, dass jedes Objekt eine Instanz von MEHRFACHEN Typen sein kann, da Sie (fälschlicherweise) davon ausgehen, dass Sie zwischen, sagen wir und Objekt und einer Funktion durch Verwendung von unterscheiden können instanceof. Da dieses letzte Beispiel zeigt eindeutig eine Funktion ist ein Objekt.

Dies ist auch wichtig, wenn Sie Vererbungsmuster verwenden und die Nachkommen eines Objekts mit anderen Methoden als der Ententypisierung bestätigen möchten.

Hoffe, das hilft jedem beim Erkunden instanceof.

webnesto
quelle
Noch beeindruckender ist, dass Sie nach dem Erben des Prototyps SubFoo.prototype = new Foo();weitere Methoden hinzufügen können, und die subfoo instanceof FooPrüfung wird weiterhin bestandensubfoo instanceof SubFoo
Cory Danielson
87

Die anderen Antworten hier sind richtig, aber sie befassen sich nicht mit der instanceoftatsächlichen Funktionsweise, was für einige Sprachanwälte da draußen von Interesse sein kann.

Jedes Objekt in JavaScript verfügt über einen Prototyp, auf den über die __proto__Eigenschaft zugegriffen werden kann. Funktionen haben auch eine prototypeEigenschaft, die die Initiale __proto__für alle von ihnen erstellten Objekte ist. Wenn eine Funktion erstellt wird, erhält sie ein eindeutiges Objekt für prototype. Der instanceofBediener verwendet diese Eindeutigkeit, um Ihnen eine Antwort zu geben. So instanceofkönnte es aussehen, wenn Sie es als Funktion geschrieben haben.

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

Dies ist im Wesentlichen eine Umschreibung von ECMA-262 Edition 5.1 (auch als ES5 bekannt), Abschnitt 15.3.5.3.

Beachten Sie, dass Sie jedes Objekt der prototypeEigenschaft einer Funktion neu zuweisen und die __proto__Eigenschaft eines Objekts nach der Erstellung neu zuweisen können . Dies gibt Ihnen einige interessante Ergebnisse:

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
quelle
5
Es ist erwähnenswert, dass die direkte Manipulation der __proto__Eigenschaft " " im IE nicht zulässig ist. Wenn ich mich richtig erinnere, ist die direkte Manipulation des Eigentums auch nicht in der ECMA-Spezifikation enthalten, daher ist es wahrscheinlich eine schlechte Idee, es für andere Aufgaben als für akademische Zwecke zu verwenden.
Webnesto
@webnesto, das stimmt, Proto ist nicht in der Spezifikation. Ich wusste nicht, dass IE es nicht unterstützt. Wenn Sie von direkter Manipulation sprechen, meinen Sie damit, dass sie überhaupt keinem JS-Code ausgesetzt ist oder einfach nicht beschreibbar ist?
Jay Conrod
2
Bei älteren Versionen nicht 100% sicher. Klingt von hier aus ( stackoverflow.com/questions/8413505/proto-for-ie9-or-ie10 ), dass es in IE9 zumindest lesbar ist (obwohl es nicht veränderbar ist). Es ist auch bemerkenswert, dass es so klingt, als würden Browser es vollständig löschen.
Webnesto
4
Es ist wichtig, die implizite Existenz der Proto- Eigenschaft zu verstehen, unabhängig davon, ob sie für Benutzercode zugänglich ist oder nicht. +10 Wenn ich die Spezifikation zitieren könnte, ist dies genau das, wonach ich hierher gekommen bin.
Mike Edwards
3
Um den Prototyp-Link zu erhalten, verwenden Sie Object.getPrototypeOf(o). Dies __proto__entspricht dem von Ihnen beschriebenen, entspricht jedoch ECMAScript.
Froginvasion
45

Ich denke, es ist erwähnenswert, dass instanceof durch die Verwendung des Schlüsselworts "new" beim Deklarieren des Objekts definiert wird. Im Beispiel von 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)

Was er nicht erwähnte, ist dies;

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

Wenn Sie "new" angeben, wird der Endstatus der String-Konstruktorfunktion tatsächlich in die Variable color1 kopiert, anstatt nur den Rückgabewert festzulegen. Ich denke, dies zeigt besser, was das neue Schlüsselwort bewirkt.

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'

Wenn Sie "new" verwenden, wird der deklarierten Variable der Wert "this" innerhalb der Funktion zugewiesen, während bei Nichtverwendung stattdessen der Rückgabewert zugewiesen wird.

Stephen Belanger
quelle
2
Es ist nicht sinnvoll, newmit einem der JavaScript-Typen zu arbeiten, was die akzeptierte Antwort für Anfänger viel verwirrender macht. text = String('test')und options = {}werden nicht von instanceofsondern getestet , sondern mit typeof.
Ryan
3
Date.getTime () // fehlschlagen. Ja, neu ist wichtig.
Stephen Belanger
1
Versuchen Sie, das in einer Konsole auszuführen. Sie erhalten eine Fehlermeldung, da getTime () nicht vorhanden ist. Sie müssen neu verwenden.
Stephen Belanger
8

Und Sie können es wie folgt zur Fehlerbehandlung und zum Debuggen verwenden:

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
quelle
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
quelle
3

Was ist es?

Javascript ist eine prototypische Sprache, dh es werden Prototypen für die Vererbung verwendet. Der instanceofOperator testet, ob der prototypeEigenschaftstyp einer Konstruktorfunktion in der __proto__Kette eines Objekts vorhanden ist. Dies bedeutet, dass Folgendes ausgeführt wird (vorausgesetzt, testObj ist ein Funktionsobjekt):

obj instanceof testObj;
  1. Überprüfen Sie, ob der Prototyp des Objekts dem Prototyp des Konstruktors entspricht: obj.__proto__ === testObj.prototype >> Wenn dies der Fall ist, true instanceofwird zurückgegeben true.
  2. Klettert die Prototypenkette hinauf. Zum Beispiel: obj.__proto__.__proto__ === testObj.prototype >> Wenn dies der Fall ist, true instanceofwird zurückgegeben true.
  3. Wiederholt Schritt 2, bis der vollständige Prototyp des Objekts überprüft wurde. Wenn nirgendwo auf der Prototypkette des Objekts angepasst ist mit testObj.prototypedann instanceofwird Operator zurückkehren false.

Beispiel:

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

Welche Probleme löst es?

Es löste das Problem der bequemen Überprüfung, ob ein Objekt von einem bestimmten Prototyp stammt. Zum Beispiel, wenn eine Funktion ein Objekt empfängt, das verschiedene Prototypen haben kann. Bevor wir Methoden aus der Prototypenkette verwenden, können wir mit dem instanceofOperator überprüfen, ob sich diese Methoden auf dem Objekt befinden.

Beispiel:

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

Hier wird in der talk()Funktion zunächst geprüft, ob sich der Prototyp auf dem Objekt befindet. Danach wird die entsprechende Methode zur Ausführung ausgewählt. Wenn Sie diese Prüfung nicht durchführen, kann dies dazu führen, dass eine nicht vorhandene Methode ausgeführt wird und somit ein Referenzfehler auftritt.

Wann ist es angebracht und wann nicht?

Wir haben das schon durchgesehen. Verwenden Sie es, wenn Sie den Prototyp eines Objekts überprüfen müssen, bevor Sie etwas damit tun.

Willem van der Veen
quelle
1
Ich denke, Sie wollten etwas anderes schreiben als " Der Prototyp einer Konstruktorfunktion erscheint irgendwo in der Prototyp-Eigenschaft eines Konstruktors "
Bergi
Vielleicht möchten Sie erwähnen, dass es angemessener wäre, wenn die Personen eine Schnittstelle gemeinsam nutzen und beide Methoden benennen PersonX.prototype.talk, damit die talkFunktion einfach ausgeführt werden kann person.talk().
Bergi
Sie haben es richtig aktualisiert, daher ist die Definition besser. Vielen Dank für den Hinweis!
Willem van der Veen
Auch nicht __proto__in der Dokumentation verwenden, es ist veraltet - schreiben Sie Object.getPrototype()stattdessen
Bergi
1

Auf die Frage "Wann ist es angebracht und wann nicht?", Meine 2 Cent:

instanceofist im Produktionscode selten nützlich, aber in Tests, in denen Sie behaupten möchten, dass Ihr Code Objekte des richtigen Typs zurückgibt / erstellt. Indem Sie explizit angeben, welche Arten von Objekten Ihr Code zurückgibt / erstellt, werden Ihre Tests leistungsfähiger als Werkzeug zum Verstehen und Dokumentieren Ihres Codes.

Andrew Magee
quelle
1

instanceofist nur syntaktischer Zucker für 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 hängt nur vom Prototyp eines Konstruktors eines Objekts ab.

Ein Konstruktor ist nur eine normale Funktion. Genau genommen ist es ein Funktionsobjekt, da alles ein Objekt in Javascript ist. Und dieses Funktionsobjekt hat einen Prototyp, weil jede Funktion einen Prototyp hat.

Ein Prototyp ist nur ein normales Objekt, das sich innerhalb der Prototypenkette eines anderen Objekts befindet. Das bedeutet, in der Prototypenkette eines anderen Objekts zu sein, macht ein Objekt zu einem Prototyp:

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

Der instanceofOperator sollte vermieden werden, da er Klassen fälscht, die in Javascript nicht vorhanden sind. Trotz des classSchlüsselworts auch nicht in ES2015, da classes wieder nur syntaktischer Zucker für ... ist, aber das ist eine andere Geschichte.


quelle
1
Die allererste Zeile ist falsch! Weitere Informationen finden Sie hier : "isPrototypeOf () unterscheidet sich vom Operator instanceof. Im Ausdruck" object instanceof AFunction "wird die Objektprototypkette gegen AFunction.prototype und nicht gegen AFunction selbst geprüft."
Yan Foto
1
@ Yan Und ist immer noch instanceofabgeleitet von isPrototypeOf. Ich nenne das syntaktischen Zucker. Und Ihre MDN-Quelle ist ein Witz, nicht wahr?
Nein, es ist kein Witz und sollte es auch nicht sein, aber immer noch die falsche Verbindung. Ich sollte diesen hier veröffentlichen lassen. Ich möchte nicht auf technische Aspekte eingehen, instanceof mache aber nicht das Gleiche wie isPrototypeOf. Das ist es.
Yan Foto
0

Ich habe gerade eine reale Anwendung gefunden und werde sie jetzt öfter verwenden, denke ich.

Wenn Sie jQuery-Ereignisse verwenden, möchten Sie manchmal eine allgemeinere Funktion schreiben, die auch direkt (ohne Ereignis) aufgerufen werden kann. Mit können Sie instanceofüberprüfen, ob der erste Parameter Ihrer Funktion eine Instanz von ist, jQuery.Eventund entsprechend reagieren.

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

In meinem Fall musste die Funktion entweder für alle (über ein Klickereignis auf einer Schaltfläche) oder nur für ein bestimmtes Element etwas berechnen. Der Code, den ich verwendet habe:

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