ES6 rief sofort die Pfeilfunktion auf

149

Warum funktioniert dies in einer Node.jsKonsole (getestet in 4.1.1 und 5.3.0), aber nicht im Browser (getestet in Chrome)? Dieser Codeblock sollte eine anonyme Funktion erstellen und aufrufen, die protokolliert Ok.

() => {
  console.log('Ok');
}()

Auch wenn das oben genannte in Node funktioniert , funktioniert dies nicht:

n => {
  console.log('Ok');
}()

Noch dies:

(n) => {
  console.log('Ok');
}()

Seltsam ist, dass der Parameter beim Hinzufügen tatsächlich einen SyntaxErrorauf den sofort aufrufenden Teil auslöst.

XCS
quelle
8
Gute Frage. Beide parametrisierten Versionen arbeiten mit Babel
CodingIntrigue
2
Funktioniert aus Interesse (n => { console.log("Ok"); })();?
CodingIntrigue
Ja (n => { console.log("Ok"); })()funktioniert sogar in der Chrome Dev Console
XCS
und so, 3 Jahre später, lautet die Antwort? sicherlich sollte eine der 3 Antworten unten akzeptiert werden ?!
Joedotnot
@joedotnot Ich habe keine klare Antwort bekommen, meistens war es eine seltsame Implementierung in Node.js. Es sieht so aus, als ob in der neuesten Version Node.jsder ersten Version nicht mehr funktioniert.
XCS

Antworten:

194

Sie müssen es zu einem Funktionsausdruck anstelle einer Funktionsdefinition machen, die keinen Namen benötigt und es zu einem gültigen JavaScript macht.

(() => {
  console.log('Ok');
})()

Ist das Äquivalent von IIFE

(function(){
   console.log('Ok')
})();

Und der mögliche Grund, warum dies in Node.js, aber nicht in Chrome funktioniert, ist, dass sein Parser es als selbstausführende Funktion interpretiert

function() { console.log('hello'); }();

funktioniert gut in Node.jsDies ist ein Funktionsausdruck und Chrome und Firefox und die meisten Browser interpretieren es so. Sie müssen es manuell aufrufen.

Die am weitesten verbreitete Methode, den Parser anzuweisen, einen Funktionsausdruck zu erwarten, besteht darin, ihn in Parens zu verpacken, da Parens in JavaScript keine Anweisungen enthalten können. Wenn der Parser auf das Funktionsschlüsselwort stößt, kann er es zu diesem Zeitpunkt als Funktionsausdruck und nicht als Funktionsdeklaration analysieren.

In Bezug auf die parametrisierte Version wird dies funktionieren.

((n) => {
  console.log('Ok');
})()
Leere
quelle
4
Das erste Beispiel funktioniert Node.jsund protokolliert den Wert tatsächlich. Meine Frage ist, warum es funktioniert? Und warum nicht, wenn ich den Parameter hinzufüge?
XCS
1
Ich bin ziemlich vertraut mit IIFEs und weiß, wie ich meinen Code reparieren kann. Ich war nur neugierig, warum zum Beispiel mein IIFEnicht funktioniert, wenn der nParameter hinzugefügt wird, obwohl es ohne den Parameter funktioniert.
XCS
3
Ich habe nicht abgestimmt, aber die Frage ist, warum die parametrisierte Version in Node nicht funktioniert, wenn genau dieselbe Definition ohne Parameter funktioniert - es wird nicht nach dem Unterschied zwischen Node / Chrome-Implementierungen anonymer Funktionen
gefragt
1
Es ist gut zu wissen, aber es beantwortet nicht die Frage, wie zuvor erwähnt, - warum funktioniert die parametrisierte Version in Node nicht, wenn genau die gleiche Definition ohne Parameter funktioniert
jkris
Aber was entspricht den function(){}()Pfeilfunktionen? Wenn ich möchte, dass das Funktionsobjekt exponierte Funktionsausdrücke davor schützt.
Dabadaba
18

Keines davon sollte ohne Klammern funktionieren.

Warum?

Denn laut Spezifikation:

  1. ArrowFunction wird unter AssignmentExpression aufgelistet
  2. Die LHS einer CallExpression muss eine MemberExpression , SuperCall oder CallExpression sein

Eine ArrowFunction kann sich also nicht auf der linken Seite einer CallExpression befinden .


Was dies bedeutet effektiv, wie =>interpretiert werden sollte, ist , dass es auf die gleiche Art von Niveau als Zuweisungsoperatoren arbeitet =, +=usw.

Bedeutung

  • x => {foo}() nicht werden(x => {foo})()
  • Der Dolmetscher versucht es als zu interpretieren x => ({foo}())
  • Somit ist es immer noch ein SyntaxError
  • Der Interpreter entscheidet also, dass das (falsch gewesen sein muss und löst einen SyntaxError aus

Es gab auf Babel ein Fehler über sie hier auch.

Paul S.
quelle
Das sind einige gültige Punkte, aber wenn ich versuche, die erste funktionierende Version durch zu ersetzen, funktioniert () => ({console.log('Ok')}())sie nicht mehr. Es interpretiert es also nicht wirklich so.
XCS
@Cristy Es ist keine gültige Pfeilfunktionsproduktion. Es wird angenommen, dass Sie versuchen, ein Objekt mit einem Objektliteral (in Klammern eingeschlossen) zu erstellen, und es console.log(...)ist kein gültiger Schlüsselname.
thefourtheye
@Cristy: Ja, ich denke, der Interpretationsteil des obigen (das "Bedeutung" -Bit) ist möglicherweise nicht ganz korrekt, aber die Spezifikationsteile sind soweit ich das beurteilen kann. Es passt auch zu dem Fehler, den ich von V8 bekomme: SyntaxError: Unexpected token ((zeigt auf das (in ()am Ende, nicht auf das (in console.log(...)).
TJ Crowder
@TJCrowder Sie haben Recht, ich werde das streichen, da es die Fehlermeldung ändert und das, was ich zu sagen versuche, nicht übermittelt wird (dh (der Dolmetscher hat nach anstrengenden Versuchen, eine gültige Interpretation zu finden, aufgegeben und geht "das muss dann falsch sein"), was sowieso falsch sein kann, weil ich nicht weiß, wie der Dolmetscher wirklich geschrieben ist
Paul S.
Ich frage mich, ob es an dieser Position kein gültiges Token ist. Würde es nicht versuchen, ein Semikolon einzufügen?
thefourtheye
2

Der Grund für diese Probleme ist, dass die Konsole selbst versucht, den globalen Bereich des Kontexts zu emulieren, auf den Sie gerade abzielen. Es wird auch versucht, Rückgabewerte von Anweisungen und Ausdrücken zu erfassen, die Sie in die Konsole schreiben, sodass diese als Ergebnisse angezeigt werden. Nimm zum Beispiel:

> 3 + 2
< 5

Hier wird es so ausgeführt, als wäre es ein Ausdruck, aber Sie haben es so geschrieben, als wäre es eine Aussage. In normalen Skripten würde der Wert verworfen, aber hier muss der Code intern entstellt werden (wie das Umschließen der gesamten Anweisung mit einem Funktionskontext und einer returnAnweisung), was alle möglichen seltsamen Effekte verursacht, einschließlich der Probleme, die auftreten.

Dies ist auch einer der Gründe, warum ein Teil des bloßen ES6-Codes in Skripten einwandfrei funktioniert, in der Chrome Dev Tools-Konsole jedoch nicht.

Versuchen Sie dies in der Node- und Chrome-Konsole auszuführen:

{ let a = 3 }

In Node oder einem <script>Tag funktioniert es einwandfrei, aber in der Konsole gibt es Uncaught SyntaxError: Unexpected identifier. Außerdem erhalten Sie einen Link zur Quelle, in dessen Form VMxxx:1Sie klicken können, um die ausgewertete Quelle zu überprüfen. Diese wird angezeigt als:

({ let a = 3 })

Warum hat es das getan?

Die Antwort ist, dass Ihr Code in einen Ausdruck konvertiert werden muss, damit das Ergebnis an den Aufrufer zurückgegeben und in der Konsole angezeigt werden kann. Sie können dies tun, indem Sie die Anweisung in Klammern setzen, wodurch sie zu einem Ausdruck wird, der obige Block jedoch auch syntaktisch falsch ist (ein Ausdruck kann keine Blockdeklaration haben).

Die Konsole versucht, diese Randfälle zu beheben, indem sie mit dem Code klug umgeht, aber das geht meiner Meinung nach über den Rahmen dieser Antwort hinaus. Sie können einen Fehler melden, um festzustellen, ob dies behoben werden kann.

Hier ist ein gutes Beispiel für etwas sehr Ähnliches:

https://stackoverflow.com/a/28431346/46588

Der sicherste Weg, um Ihren Code zum Laufen zu bringen, besteht darin, sicherzustellen, dass er als Ausdruck ausgeführt werden kann, und den Quelllink zu überprüfen, um SyntaxErrorfestzustellen, wie der tatsächliche Ausführungscode lautet, und daraus eine Lösung zurückzuentwickeln. Normalerweise bedeutet dies ein Paar strategisch platzierter Klammern.

Kurz gesagt: Die Konsole versucht, den globalen Ausführungskontext so genau wie möglich zu emulieren. Aufgrund der Einschränkungen der Interaktion mit der v8-Engine und der JavaScript-Semantik ist dies manchmal schwierig oder unmöglich zu lösen.

Klemen Slavič
quelle
1
Das ist der ganze Punkt, der Parameter ist mir wichtig, aber er funktioniert nicht mit dem Parametersatz.
XCS
OK, ich verstehe deinen Standpunkt. Der Unterschied liegt in der Art und Weise, wie die Chrome Dev Tools-Konsole Ihren Code tatsächlich ausführt. Ich werde die Antwort bearbeiten, um dies widerzuspiegeln.
Klemen Slavič
0

Ich habe eine Frage wie diese gestellt:

@getify Ich habe folgende Frage: Um ein # IIFE-Muster zu erstellen, verwenden wir Parans um eine Funktionsdeklaration, um es in einen Funktionsausdruck umzuwandeln und dann aufzurufen. Warum brauchen wir in Pfeilfunktion IIFEs Parans?! Ist die Pfeilfunktion nicht schon standardmäßig ein Ausdruck?!

und das ist die Antwort von Kyle Simpson:

Eine Pfeilfunktion ist ein Ausdruck, aber wir benötigen umgebende Parens b / c mit "Operator-Priorität" (sorta), damit die letzten Parens zum Aufrufen des Pfeil-IIFE für die gesamte Funktion und nicht nur für das letzte Token ihres Körpers gelten .

x => console.log(x)(4)

vs.

(x => console.log(x))(4)

- getify (@getify) 12. Juni 2020

Ershad Qaderi
quelle
Meine Frage war, warum es bei einigen Compilern und nicht bei anderen funktioniert hat.
XCS
Das liegt daran, dass sich verschiedene Compiler in einigen Details unterschiedlich verhalten, genau wie verschiedene Browser, die natürlich unterschiedliche Compiler haben
Ershad Qaderi
Sie haben Recht, sie verhalten sich zwar anders, aber die JavaScript-Spezifikationen sind für alle gleich. Ich war neugierig, welches richtig war, was die JS-Spezifikation zu diesem Fall sagt und insbesondere wie könnte es sein, dass es ohne Argument funktioniert, aber nicht mit Argument. Ich suchte nach einer technischeren Antwort.
XCS
Ihr Beispiel ist ziemlich offensichtlich, im ersten Fall sollte es tatsächlich aufgerufen werden console.log(x)(4).
XCS
Ich rate hier nur, aber ich denke, es ist sehr vernünftig, es so zu erklären: Wenn wir in Pfeilfunktionsausdrücken keinen Parameter verwenden, müssen wir die Parens verwenden, und das macht der Engine sehr deutlich, dass dies ein Pfeil ist Funktionsausdruck, aber wenn wir einen einzelnen Parameter haben, sind die Parens willkürlich, was möglicherweise nicht sehr klar ist, dass dies eine Funktion ist und die jene Engine verwirrt. Um die Verwirrung zu beseitigen, müssen wir ein Paar Parens um den gesamten Funktionsausdruck setzen
Ershad Qaderi