Warum geben in JavaScript sowohl Object instanceof Function
als auch Function instanceof Object
true
zurück?
Ich habe es in Safari WebInspector versucht.
Es hat eine Weile gedauert, bis ich herausgefunden habe, aber es lohnt sich wirklich. Lassen Sie uns zunächst sehen, wie instanceof
funktioniert.
Zitieren aus MDN ,
Der Operator
instanceof
testet, ob ein Objekt dieprototype
-Eigenschaft eines Konstruktors in seiner Prototypkette hat.
[instanceof]
Lassen Sie uns nun sehen, wie instanceof
durch die ECMA 5.1-Spezifikation definiert ist.
Die Produktion
RelationalExpression: RelationalExpression instanceof ShiftExpression
wird wie folgt bewertet:
- Sei
lref
das Ergebnis der Auswertung vonRelationalExpression
.- Sei
lval
GetValue(lref)
.- Sei
rref
das Ergebnis der Auswertung vonShiftExpression
.- Sei
rval
GetValue(rref)
.- Wenn
Type(rval)
nicht Object ist, lösen Sie eineTypeError
-Ausnahme aus.- Wenn
rval
keine interne Methode[[HasInstance]]
hat, geben Sie eineTypeError
-Ausnahme aus.- Gibt das Ergebnis des Aufrufs der internen Methode
[[HasInstance]]
vonrval
mit dem Argumentlval
zurück.
Zuerst werden die Ausdrücke für die linke und rechte Seite ausgewertet (GetValue
), und das Ergebnis auf der rechten Seite sollte eine Object mit [[HasInstance]]
interner Methode sein. Nicht alle Objekte verfügen über eine [[HasInstance]]
interne Methode, sondern über Funktionen. Zum Beispiel wird Folgendes fehlschlagen
console.log(Object instanceof {});
# TypeError: Expecting a function in instanceof check, but got #<Object>
[[HasInstance]]
Lassen Sie uns nun sehen, wie [[HasInstance]]
in der ECMA 5.1-Spezifikation definiert wurde.
Angenommen,
F
ist ein Funktionsobjekt.Wenn die interne Methode
[[HasInstance]]
vonF
mit dem WertV
aufgerufen wird, werden die folgenden Schritte ausgeführt:
- Wenn
V
kein Objekt ist, geben Siefalse
zurück.- Sei
O
das Ergebnis des Aufrufs der internen Methode[[Get]]
vonF
mit dem Eigenschaftennamen"prototype"
.- Wenn
Type(O)
nicht Object ist, lösen Sie eineTypeError
-Ausnahme aus.- Wiederholen
- Sei
V
der Wert der internen Eigenschaft[[Prototype]]
vonV
.- Wenn
V
null
ist, geben Siefalse
zurück.- Wenn sich
O
undV
auf dasselbe Objekt beziehen, geben Sietrue
zurück.
Es ist so einfach. Nehmen Sie die prototype
-Eigenschaft von F
und vergleichen Sie sie mit der [[Prototype]]
-internen Eigenschaft von O
, bis sie null
wird oder prototype
von F
mit O
identisch ist.
[[prototype]]
interne EigenschaftZuerst sehen wir uns die interne Eigenschaft [[prototype]]
an ,
Alle Objekte haben eine interne Eigenschaft namens
[[Prototype]]
. Der Wert dieser Eigenschaft ist entwedernull
oder ein Objekt und wird zum Implementieren der Vererbung verwendet. Ob ein natives Objekt ein Host-Objekt als[[Prototype]]
haben kann, hängt von der Implementierung ab. Jede[[Prototype]]
-Kette muss eine endliche Länge haben (das heißt, ausgehend von einem beliebigen Objekt muss der rekursive Zugriff auf die interne Eigenschaft[[Prototype]]
letztendlich zu einemnull
-Wert führen).
Hinweis: Wir können diese interne Eigenschaft mit der Funktion Object.getPrototypeOf
erhalten.
prototype
-Eigenschaft[[HasInstance]]
spricht auch von einer anderen Eigenschaft namens prototype
, die für die Function
-Objekte spezifisch ist.
Der Wert der
prototype
-Eigenschaft wird zum Initialisieren der[[Prototype]]
-internen Eigenschaft eines neu erstellten Objekts verwendet, bevor das Function-Objekt als Konstruktor für das neu erstellte Objekt aufgerufen wird.
Das heißt, wenn ein Funktionsobjekt als Konstruktor verwendet wird, wird ein neues Objekt erstellt, und das neue Objekt wird mit seinem [[Prototype]]
mit dieser prototype
-Eigenschaft initialisiert. Zum Beispiel,
function Test() {}
Test.prototype.print = console.log;
console.log(Object.getPrototypeOf(new Test()) === Test.prototype);
# true
Kommen wir nun zur eigentlichen Frage zurück. Nehmen wir den ersten Fall
console.log(Object instanceof Function);
# true
Zuerst wird Function.prototype
abgerufen und versucht zu finden, ob sich dieses Objekt in der Prototyphierarchie von Object
befindet. Mal sehen, wie sich das herausstellt
console.log(Function.prototype);
# [Function: Empty]
console.log(Object.getPrototypeOf(Object));
# [Function: Empty]
console.log(Object.getPrototypeOf(Object) === Function.prototype);
# true
Da Function.prototype
mit der internen Eigenschaft [[Prototype]]
der Object
übereinstimmt, wird true
zurückgegeben.
Nehmen wir nun den zweiten Fall
console.log(Function instanceof Object);
# true
console.log(Object.prototype);
# {}
console.log(Object.getPrototypeOf(Function));
# [Function: Empty]
console.log(Object.getPrototypeOf(Function) === Object.prototype);
# false
console.log(Object.getPrototypeOf(Object.getPrototypeOf(Function)));
# {}
Object.getPrototypeOf(Object.getPrototypeOf(Function)) === Object.prototype
# true
Hier erhalten wir zuerst den Object.prototype
, der {}
ist. Jetzt versucht es herauszufinden, ob dasselbe Objekt {}
in der Prototypkette der Function
vorhanden ist. Unmittelbare übergeordnete Komponente von Function
ist und eine leere Funktion.
console.log(Object.getPrototypeOf(Function));
# [Function: Empty]
Es ist nicht das Gleiche wie Object.prototype
console.log(Object.getPrototypeOf(Function) === Object.prototype);
# false
Aber der [[HasInstance]]
-Algorithmus hört hier nicht auf. Es wiederholt sich und steht eine weitere Ebene auf
console.log(Object.getPrototypeOf(Object.getPrototypeOf(Function)));
# {}
Und das ist dasselbe wie Object.prototype
. Deshalb wird true
zurückgegeben.
Von MDN :
Der Operator instanceof prüft, ob ein Objekt die Prototypeneigenschaft eines Konstruktors in seiner Prototypkette hat.
Im Wesentlichen wird geprüft, ob Object
(nicht eine Instanz von Object
, sondern der Konstruktor selbst) eine Instanz von Function.constructor
irgendwo in seiner Prototypkette hat.
Und in der Tat:
> Function.__proto__.__proto__ === Object.prototype
true
> Object.__proto__ === Function.prototype
true
Dies erklärt, warum Object instanceof Function
ebenso wie umgekehrt.
ALLE Objekte haben eine interne Eigenschaft namens [[Prototyp]]. Der Wert dieser Eigenschaft ist entweder null oder ein Objekt und wird zur Implementierung der Vererbung verwendet. Wenn Sie versuchen, einen Schlüssel in einem Objekt nachzuschlagen, und dieser nicht gefunden wird, sucht JavaScript in der Prototypkette nach dem Schlüssel.
Der Funktionskonstruktor erstellt neue Funktionsobjekte (Instanzen des Funktionskonstruktors). Die Prototypeneigenschaft ist spezifisch für Funktionsobjekte. Der Funktionskonstruktor ist selbst ein Funktionsobjekt (Instanz des Funktionskonstruktors).
Wenn ein Funktionsobjekt als Konstruktor verwendet wird, wird ein neues Objekt erstellt, und das [[Prototyp]] des neuen Objekts wird mit der Prototypeneigenschaft des Konstruktors initialisiert.
function Dog () {}
var myCrazyDog = new Dog();
myCrazyDog.__proto__ === Dog.prototype // true
Die Sprachspezifikation ist, dass alle Objekte Instanzen des Objektkonstruktors und alle Funktionen Instanzen des Funktionskonstruktors sind.
Objektinstanz von Funktion ist wahr weil Objekt eine Funktion und somit eine Instanz von Funktion ist (Objekt ist ein Funktionsobjekt - eine Instanz des Funktionskonstruktors). Objekt erbt von Function.prototype.
console.log(Object instanceof Function) // true
console.log(Object.__proto__ === Function.prototype) // true
Objektinstanz von Objekt ist wahr , weil Objekt von Function.prototype erbt. Da Function.prototype ein Objekt ist, erbt es von Object.prototype. Die Funktionsinstanz von Object ist true , da die Funktion von Function.prototype erbt. Da Function.prototype ein Objekt ist, erbt es von Object.prototype. Die Prototypkette sieht folgendermaßen aus:
Object ---> Function.prototype ---> Object.prototype ---> null
Function ---> Function.prototype ---> Object.prototype ---> null
console.log(Object instanceof Object) // true
console.log(Object.__proto__ === Function.prototype) // true
console.log(Object.__proto__.__proto__ === Object.prototype) // true
console.log(Function instanceof Object) // true
console.log(Function.__proto__ === Function.prototype) // true
console.log(Function.__proto__.__proto__ === Object.prototype) // true
Funktionsinstanz von Funktion ist wahr . Funktion ist eine Instanz von sich selbst (natürlich, da es sich um eine Funktion und somit um eine Instanz von Function handelt). Die Prototypkette sieht folgendermaßen aus:
Function ---> Function.prototype ---> Object.prototype ---> null
console.log(Function instanceof Function) // true
console.log(Function.__proto__ === Function.prototype) // true
console.log(Function.__proto__.__proto__ === Object.prototype) // true
Denken Sie also daran, dass die Konstruktoren Function () und Object () Funktionen sind. Da es sich um Funktionen handelt, handelt es sich um Instanzen des Function () - Konstruktors, die von Function.prototype erben. Da Function.prototype ein Objekt ist, ist Function.prototype eine Instanz von Object und erbt somit von Object.prototype.
console.log(Object instance of Function) // true
console.log(Function instance of Function) // true
console.log(Function.prototype instanceof Object); // true
Die Quelle der Verwirrung in Ihrer Frage liegt in der inhärenten Doppelnatur von Funktionen * in JavaScript (ECMAScript).
Funktionen in js sind sowohl reguläre Funktionen als auch Objekte zur gleichen Zeit. Betrachten Sie sie als algorithmisch Dr. Jekyll und Mr. Hyde . Sie sehen aus wie Objekte auf der Außenseite, aber innen sind es nur Ihre guten alten Js-Funktionen mit all ihren Macken, oder vielleicht ist es umgekehrt!
JavaScript ist wirklich ein kniffliges Geschäft :)
Zurück zu Ihrer Frage, die Syntax aus dem MDN entlehnt:
object instanceof constructor
Anwenden auf die erste Anweisung in Ihrem Code:
Object instanceof Function
Hier haben Sie Object
, eine Konstruktorfunktion, die als Objektinitialisierer verwendet wird, aber da Funktionen in js ein Doppelleben führen, sind ihr objektspezifische Requisiten und Methoden zugeordnet, sodass sie auch ein Objekt sind.
Die erste Bedingung in der Anweisung wurde erfüllt. Wir müssen die andere Bedingung oder den Operanden untersuchen.
Wie Sie vielleicht bemerkt haben, ist Function
auch ein Funktionskonstruktor, aber die andere Objektseite ist für uns jetzt während der Ausführung dieser bestimmten Anweisung uninteressant.
Die syntaktischen Bedingungen sind also beide erfüllt, nämlich "Objekt" und "Konstruktor". Wir können jetzt damit fortfahren, ihre erblicheBeziehung zu untersuchen und ob zwischen ihnen eine Verbindung besteht.
Da Object
selbst eine Arbeitsfunktion ist, ist es sehr sinnvoll anzunehmen, dass ihre interne Prototyp-Requisite auf die Function.prototype
-Objektreferenz verweist, da in jsALLfunction vererben deren Requisiten und Methoden durch denselben Ort Function.prototype
.
true
ist definitiv dasONLYerwartete Ergebnis dieses Vergleichs, das vom instanceof
-Operator durchgeführt wird.
Für den anderen Fall:
Function instanceof Object
Da wir bereits festgestellt haben, dass Funktionen in js auch eine Objektseite haben. Es ist sinnvoll, dass sie ihr ausgefallenes objektspezifisches Spielzeug aus dem Object.prototype
erhalten haben und daher Instanzen des Objektkonstruktors darstellen.
Ich hoffe, dass ich mit meinen Erklärungen und Allegorien nicht zu der Verwirrung beigetragen habe. :)
*: Nicht nur Funktionen, die in js ein Doppelleben führen. Fast alle Datentypen in js haben eine dunkle Seite, die das Ausführen von Operationen und Manipulationen erleichtert.
Die schlechteste Eigenschaft ist eigentlich, dass Function eine Instanz von sich selbst ist. Function instanceof Function
gibt true zurück.
Es wird in Kannan's Das überraschend elegante Javascript-Typmodell , unter http://web.archive.org/web/20140205182624/http://vijayan.ca/blog/2012/02/ erläutert. 21/javascript-type-model
Zitat am Ende der Erklärung:
Ja, das bedeutet, dass Funktion eine Instanz von sich selbst ist (natürlich, da es sich um eine Funktion und somit um eine Instanz von Function handelt). Dies ist etwas, mit dem wir uns alle wissentlich oder nicht lange beschäftigt haben - alle Konstruktorfunktionen sind reguläre Funktionen und damit Instanzen von Function, und Function selbst ist nur die Konstruktorfunktion zum Konstruieren anderer Funktionen, also auch eine Instanz von Function.
Sehr einfache Erklärung, anders als alle Antworten
Sowohl Object als auch Function sind Konstruktoren (Typ von beiden mit Rückgabe "Funktionsobjekte"), und beide werden vom Function Constructor erstellt. Die __proto__-Eigenschaft beider zeigt auf das 'Function.prototype'-Objekt.
SCHNELLE ERKLÄRUNG: Die __proto__-Eigenschaft eines Objekts (z. B. p1, bei der es sich um einen Personentyp handelt) zeigt auf das Prototyp des Konstruktors (z. B. person.prototype). Wieder zeigt das __proto__ in der Prototypkette auf das Objekt "Object.prototype".
VOR DEM LESEN WEITERER DRUCKDETAILS AUF CHROME CONSOLE console.dir (Objekt), console.dir (Funktion)
KEEP IN MIND, Funktionskonstruktoren und auch Objekte, so dass Sie beide .prototype- und __proto__-Eigenschaften sehen. In allen Instanzobjekten (z. B. p1) finden Sie nur die Eigenschaft __proto__. Das __proto__ ist ein Accessor für die versteckte Eigenschaft [[Prototye]] und der beste Weg, um es zu erhalten, ist Object.getPrototypeOf (p1), da __proto__ depricated ist.
(p1 Instanz von Person) Hier prüft der Operator, ob der Prototyp der Konstrukteur Person in der Prototypkette des Objekts p1 enthalten ist. Beachten Sie, dass der erste Wert ein Instanzobjekt (p1) und der zweite Wert ein Konstruktor (Person) ist.
Lets analyse => Funktionsinstanz von Object.
Das __proto __.__ proto__ des Funktionsobjekts zeigt auf Object.prototype. Also ist es wahr
Lassen Sie uns analysieren => Objektinstanz der Funktion.
__Proto__ des Objektobjekts zeigt auf Function.prototype, also ist es wahr
Hoffe das hilft.