In Chrome und Firefox
%Vor% prüft auf 'undefined'
.
Aber
%Vor%gibt einen Fehler aus:
%Vor% Das zerstört die Vorstellungen, die ich von der Vertretbarkeit von Ausdrücken habe! Bis jetzt kannte ich keine Bedingungen, für die foo
und (function() { return foo; })()
nicht gleich sind.
Ist das Standardverhalten? Wenn ja, wäre es hilfreich, den relevanten Teil des ECMAScript-Standards zu zitieren.
BEARBEITEN:
Ein anderes Beispiel:
%Vor% Ich hätte erwartet, dass (foo)
und (foo + 0)
einen Fehler auslösen.
Aber der erste hat keinen Fehler; der zweite tut.
Grundsätzlich überprüft der Operator typeof
, ob eine Variable¹ nicht aufgelöst werden kann und gibt "undefined"
zurück. Das heißt, typeof
gibt einen definierten Wert für nicht deklarierte Variablen¹ zurück, bevor der Algorithmus GetValue
erreicht wird, der auf nicht deklarierte Variablen¹ verweist .
Zitieren ECMAScript 5.1 § 11.4.3 Der Typ des Operators (Hervorhebung hinzugefügt):
11.4.3 Der Typ des Operators
Die Produktion UnaryExpression :
typeof
UnaryExpression ist wie folgt bewertet:
- Sei val das Ergebnis der Evaluierung von UnaryExpression .
Wenn Typ ( val ) Referenz , dann
2.1. Wenn IsUnresolvableReference ( val )
true
ist, geben Sie"undefined"
.2.2 Es sei val GetValue ( val ).
Gibt eine Zeichenfolge zurück, die durch Typ ( val ) gemäß Tabelle 20 .
Auf der anderen Seite die return-Anweisung - wie die meisten Operatoren und Anweisungen, die den Wert aus dem Bezeichner lesen ( s) - ruft immer GetValue
auf, was nicht lösbare Kennungen (nicht deklarierte Variablen) auslöst. Zitieren ECMAScript 5.1 § 8.7.1 GetValue (V) (Hervorhebung hinzugefügt):
8.7.1 GetValue (V)
- Wenn Typ (V) keine Referenz ist, geben Sie V zurück.
- Basis ist das Ergebnis des Aufrufs von GetBase (V).
- Wenn IsUnresolvableReference (V), werfen Sie eine
ReferenceError
Ausnahme aus.
Analysieren Sie jetzt den Code:
%Vor% Dieser Code wird ein Funktionsobjekt instanziieren, ausführen und nur dann wird typeof
mit dem Rückgabewert der Funktion arbeiten (Funktionsaufruf dauert Priorität über den Operator typeof
.
Daher wird der Code ausgegeben, während die return
-Anweisung der IIFE ausgewertet wird, bevor die Operation typeof
ausgewertet werden kann.
Ein ähnliches, aber einfacheres Beispiel:
%Vor% Die Addition wird vor typeof
ausgewertet. Dies führt zu einem Fehler, wenn der Zusatzoperator GetValue
auf foo
aufruft, bevor typeof
ins Spiel kommt .
Jetzt:
%Vor% Es wird kein Fehler ausgegeben, da der Gruppierungsoperator (Klammern) nichts per se "evaluiert" erzwingt nur Vorrang. Genauer gesagt ruft der Gruppierungsoperator nicht GetValue
auf. Im obigen Beispiel wird eine (nicht auflösbare) Referenz zurückgegeben.
Die kommentierte ES5.1-Spezifikation fügt sogar eine Anmerkung hinzu:
HINWEIS Dieser Algorithmus wendet
GetValue
nicht auf das Ergebnis der Auswertung von Expression an . Die Hauptmotivation dafür ist, dass Operatoren wiedelete
undtypeof
auf eingeklammerte Ausdrücke angewendet werden können.
NB Ich habe diese Antwort geschrieben mit dem Schwerpunkt, eine einfache und verständliche Erklärung zu geben, den Fachjargon auf ein Minimum zu beschränken, aber immer noch ausreichend klar zu sein und die geforderten ECMAScript-Standardreferenzen bereitzustellen hoffe, eine hilfreiche Ressource für Entwickler zu sein, die Schwierigkeiten haben, den typeof
-Operator zu verstehen.
¹ Der Begriff "Variable" wird verwendet, um das Verständnis zu erleichtern. Ein korrekterer Begriff wäre identifier , der nicht nur in eine Lexikalische Umgebung eingeführt werden kann durch Variablendeklarationen, aber auch Funktionsdeklarationen, Formalparameter, Aufruf einer Funktion ( arguments
), with
/ catch
Blöcke, Zuordnung einer Eigenschaft zum globalen Objekt, let
und const
Anweisungen (ES6), und möglicherweise ein paar andere Möglichkeiten.
Ist das Standardverhalten?
Ja. typeof gibt keinen Fehler aus, weil nur ein Wert zurückgegeben wird angegeben . Wie jedoch andere Antworten gesagt haben, schlägt der Code bei der Auswertung des Operanden fehl.
Wenn ja, wäre es hilfreich, den relevanten Teil des ECMAScript-Standards zu zitieren.
Bei der Auswertung des Funktionsausdrucks wird der Versuch, den Wert von foo aufzulösen (so dass er zurückgegeben werden kann), das interne GetValue Methode mit Argument foo . Da foo jedoch nicht deklariert oder anderweitig erstellt wurde, wird ein Referenzfehler ausgelöst.
Im Falle von:
%Vor% "(" und ")" sind Punctuators B. eine (möglicherweise leere) Parameterliste angeben, wenn eine Funktion wie foo(a, b)
aufgerufen wird, oder ein Ausdruck, der ausgewertet werden soll, z if (x < 0)
und so weiter.
Im Fall von typeof (foo)
bezeichnen sie einfach die Bewertung von foo , bevor Sie den Operator typeof anwenden. So wird foo , das ein gültiger Bezeichner ist, an typeof übergeben, wobei der obige Link, der versucht, es aufzulösen, nicht feststellen kann, ob es eine nicht löschbare Referenz "undefined"
zurück.
Im Falle von:
%Vor% Die Klammern bewirken, dass der Ausdruck foo + 0
zuerst ausgewertet wird. Wenn der Wert von foo abgerufen wird, wird ein Referenzfehler ausgelöst, sodass typeof nicht funktioniert. Beachten Sie, dass ohne die Klammern:
aufgrund der Vorrangstellung des Operators: typeof foo
gibt die Zeichenfolge "undefined"
zurück, so dass +
zu Additionsoperator weil eines der Argumente ein String ist, führt es eine Verkettung durch (die String-Version von addition, nicht die mathematische Version), also wird 0
in den String% konvertiert co_de% und verkettet zu "0"
, reselling in der Zeichenfolge "undefined"
.
Zu jeder Zeit, zu der die Auswertung eines Ausdrucks mit einer nicht auflösbaren Referenz versucht wird (z. B. eine nicht deklarierte oder initialisierte Variable), wird ein Referenzfehler ausgelöst, z. B.
%Vor% löst auch einen Referenzfehler aus, um herauszufinden, was an typeof übergeben werden soll, muss der Ausdruck ausgewertet werden. Um den Operator "undefined0"
anzuwenden, muss der Wert von foo erhalten werden, und bei dem Versuch wird ein Referenzfehler ausgelöst.
Der Fehler "ReferenceError: foo ist nicht definiert" wird nicht von typeof
ausgelöst, sondern von der Funktion selbst ausgelöst. Wenn du benutzt hättest:
es hätte "number" wie erwartet zurückgegeben, aber in diesem Beispiel erreicht JavaScript nicht einmal den Punkt, an dem typeof
auf irgendwas ausgeführt wird. Sie erhalten den gleichen Fehler wie bei der Ausführung:
Wenn ich die Spezifikation durchschaue, denke ich, dass alles auf hinausläuft, wenn der betreffende Operator versucht, GetValue()
für seinen Operanden auszuführen.
typeof
versucht zuerst den Type
seines Operanden zu ermitteln. Wenn dieser Typ ein Reference
ist und IsUnresolvableReference()
, dann wird ein Fehler ausgegeben und undefined
zurückgegeben. Im Wesentlichen wird der Operand nicht vollständig ausgewertet; Wenn dies der Fall wäre, würde alles, was undefined
war, eine Exception auslösen, stattdessen wird es kurzgeschlossen und gibt eine nette, nützliche Zeichenfolge zurück.
In den Beispielen werden die selbstausführenden Funktionen und der Aufruf des Additionsoperators GetValue
ohne zuerst auf IsUnresolvableReference()
like typeof
überprüft: Sie rufen GetValue
auf und werfen eine Ausnahme , wenn die Referenz nicht aufgelöst ist ( foo
ist in unserem Fall undefined
) . (Ich denke! Das ist meine beste Schätzung beim Lesen der Spezifikation.)
Dies ist Standardverhalten. Der Operator typeof
nimmt fast eine Referenz der nächsten Variablen, die Sie übergeben.
Versuchen wir also typeof foo
.
Der Javascript-Interpreter betrachtet typeof und findet den Typ von foo
.
Jetzt versuchen wir typeof (function() { return foo })()
Der Javascript-Interpreter betrachtet typeof. Da der Ausdruck danach keine Variable ist, wertet er den Ausdruck aus. (function() { return foo })()
gibt ReferenceError
aus, weil foo
nicht definiert ist. Wenn es möglich wäre, die Referenz einer Variablen, d. H. Etwas wie (function() { return *foo })()
, zu übergeben, würde dies nicht passieren.
Hinweis: Demnach könnte man denken, dass typeof (foo)
einen Fehler werfen würde, da (foo)
keine Variable ist und ausgewertet werden muss, aber das ist falsch; typeof (foo)
gibt auch "undefined" zurück, wenn foo nicht definiert ist.
Im Wesentlichen wertet der Interpreter die nächste Variable, aber nicht den Ausdruck, in einem "sicheren" Kontext aus, so dass typeof
keinen Fehler auslöst.
Es ist ein bisschen verwirrend.
Tags und Links javascript expression language-lawyer typeof referenceerror