Ich versuche, mich mit den neuen standardisierten Funktionen auf Blockebene in ES6 vertraut zu machen, indem ich die Rohspezifikation lese. Mein oberflächliches Verständnis war:
- Funktionsdeklarationen auf Blockebene sind in ES6 zulässig.
- Sie ziehen an die Spitze des Blocks.
- Im strikten Modus sind sie außerhalb des enthaltenden Blocks nicht sichtbar.
Dies wird jedoch durch die Tatsache weiter verkompliziert, dass ein Teil dieser Semantik als “optional” und nur für Webbrowser obligatorisch angegeben ist (Anhang B). Ich möchte also folgende Tabelle ausgefüllt haben:
| Visible outside of block? | Hoisted? Up to which point? | "TDZ"? |
------------------------------------------------------------------------------------------------------------------------
| Non-strict mode, no "web extensions" | | | |
| Strict mode, no "web extensions" | | | |
| Non strict mode, with "web extensions | | | |
| Strict mode, with "web extensions" | | | |
Auch ist mir unklar, was “strikter Modus” in diesem Zusammenhang bedeutet. Diese Unterscheidung scheint eingeführt worden zu sein Anlage B3.3als Teil einiger zusätzlicher Schritte für die Laufzeitausführung einer Funktionsdeklaration:
1. If strict is false, then
...
Aber so wie ich das sehe, strict
bezieht sich auf [[Strict]]
interner Steckplatz des Funktionsobjekts. Bedeutet dies:
// Non-strict surrounding code
{
function foo() {"use strict";}
}
sollte in der obigen Tabelle als “strikter Modus” betrachtet werden? Das widerspricht jedoch meiner anfänglichen Intuition.
Bitte bedenken Sie, dass ich hauptsächlich an der ES6-Spezifikation selbst interessiert bin, unabhängig von tatsächlichen Implementierungsinkonsistenzen.
Soweit ich das beurteilen kann, strict
bezieht sich auf [[Strict]]
interner Steckplatz des Funktionsobjekts.
Nein und Ja. Es bezieht sich auf die Strenge der Funktion (oder Skript) in welchem der Block, der die Funktionsdeklaration enthält, auftritt. Nicht auf die Strenge der zu deklarierenden (oder nicht zu deklarierenden) Funktion.
Die “Web-Erweiterungen” gelten nur für schlampigen (nicht strikten) Code und nur, wenn das Aussehen der Funktionsanweisung “gesund” ist – das heißt, wenn ihr Name beispielsweise nicht mit einem formalen Parameter oder lexikalisch kollidiert deklarierte Variable.
Beachten Sie, dass es ohne die Webkompatibilitätssemantik keinen Unterschied zwischen striktem und schlampigem Code gibt. In reinem ES6 gibt es nur ein Verhalten für Funktionsdeklarationen in Blöcken.
Also haben wir im Grunde
| web-compat pure
-----------------+---------------------------------------------
strict mode ES6 | block hoisting block hoisting
sloppy mode ES6 | it's complicated ¹ block hoisting
strict mode ES5 | undefined behavior ² SyntaxError
sloppy mode ES5 | undefined behavior ³ SyntaxError
1: Siehe unten. Es wird um Warnungen gebeten.
2: Typischerweise a SyntaxError
ist geworfen
3: Die Notiz in ES5.1 §12 spricht von “erhebliche und unüberbrückbare Abweichungen zwischen den Implementierungen” (wie zum Beispiel diese). Warnungen werden empfohlen.
Wie verhält sich nun eine ES6-Implementierung mit Webkompatibilität für eine Funktionsdeklaration in einem Block in einer Sloppy-Mode-Funktion mit Legacy-Semantik?
Zunächst einmal die Es gilt immer noch die reine Semantik. Das heißt, die Funktionsdeklaration wird an die Spitze des lexikalischen Blocks gehoben.
Allerdings gibt es auch eine var
Erklärung die an die Spitze der umschließenden Funktion gehisst wird.
Und wenn die Funktionsdeklaration ausgewertet wird (im Block, als ob sie wie eine Anweisung erfüllt wurde), ist das Funktionsobjekt zugewiesen zu dieser funktionsbezogenen Variablen.
Dies wird besser durch Code erklärt:
function enclosing(…) {
…
{
…
function compat(…) { … }
…
}
…
}
funktioniert genauso wie
function enclosing(…) {
var compat₀ = undefined; // function-scoped
…
{
let compat₁ = function compat(…) { … }; // block-scoped
…
compat₀ = compat₁;
…
}
…
}
Ja, das ist etwas verwirrend, da es zwei verschiedene Bindings (gekennzeichnet mit den Indizes 0 und 1) mit demselben Namen gibt. Jetzt kann ich Ihre Fragen kurz beantworten:
Sichtbar außerhalb des Blocks?
Ja, wie ein var
. Es gibt jedoch eine zweite Bindung, die nur innerhalb des Blocks sichtbar ist.
Hochgezogen?
Ja zweimal.
Bis zu welchem Punkt?
Sowohl zur Funktion (allerdings initialisiert mit undefined
) und dem Block (mit dem Funktionsobjekt initialisiert).
“TDZ”?
Nicht im Sinne der zeitlichen Totzone einer lexikalisch deklarierten Variablen (let
/const
/class
), die auf Referenzierung wirft, nein. Aber bevor die Funktionsdeklaration bei der Ausführung des Hauptteils angetroffen wird, ist die funktionsbezogene Variable undefined
(insbesondere vor dem Block), und Sie erhalten auch eine Ausnahme, wenn Sie versuchen, ihn aufzurufen.
Nur zur Information: In ES6 wurde das oben beschriebene Verhalten nur für Blöcke in Funktionsumfängen spezifiziert. Seit ES7 gleiches gilt für Blöcke in eval
und globale Reichweiten.
Ich bin mir nicht sicher, woher deine Verwirrung kommt. Entsprechend 10.2.1 Es ist sehr klar, was “im strikten Modus” ist oder nicht. In Ihrer Probe foo
S [[Strict]]
interner Steckplatz wäre true
in der Tat und wird im strikten Modus sein, aber der Block, der es hostet, wird es nicht tun. Der erste Satz (der von Ihnen zitierte) bezieht sich auf den Hosting-Block, nicht auf den darin generierten Inhalt. Der Block in Ihrem Fragment befindet sich nicht im strikten Modus und daher gilt dieser Abschnitt für ihn.
Bitte vergessen Sie den Begriff „Heben“. Alle Funktionsdeklarationen werden verarbeitet, bevor irgendein Code ausgeführt wird. Der Geltungsbereich auf Blockebene wirkt sich darauf aus, wie die Identifikatorauflösung erfolgt (dh nichts mit “Heben” zu tun hat), eine innerhalb eines Blocks deklarierte Funktion kann außerhalb des Blocks verfügbar sein oder nicht. Oh, und Funktionsdeklarationen werden verarbeitet nach dem Variablendeklarationen, also überschreiben sie Variablen (eine spätere Zuweisung an die Variable kann das natürlich ändern…).
– RobG
15. Juli 2015 um 6:19 Uhr
Verwandt.
– Ben Aston
14. April 2020 um 16:32 Uhr