Was ist die genaue Semantik von Funktionen auf Blockebene in ES6?

Lesezeit: 7 Minuten

Was ist die genaue Semantik von Funktionen auf Blockebene in
rvidal

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.

  • 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

1646641513 975 Was ist die genaue Semantik von Funktionen auf Blockebene in
Bergi

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.

  • Hervorragende Antwort! Was würde dann passieren, wenn: (1) die Funktionsdeklaration nicht “gesund” ist? oder (2) die Funktionsdeklaration “Top-Level-Block-Level” ist (dh der enthaltende Block ist Top-Level, anstatt in einer Funktion verschachtelt zu sein)?

    – rvidal

    17. Juli 2015 um 15:39 Uhr

  • @rvidal: Dann gilt das standardmäßige Heben / Scoping auf Blockebene (in beiden Fällen) und die Funktion ist außerhalb des Blocks nicht sichtbar.

    – Bergi

    17. Juli 2015 um 15:55 Uhr

  • @rvidal: Das erste ändert nichts an meiner Antwort 🙂 Das zweite bezieht sich auf eine Errata in der ES6-Spezifikation über dieses Legacy-Verhalten, das nur für Funktionsbereiche gilt (was es nicht sollte), aber ich habe das nicht unterschieden meine antwort jedenfalls. Übrigens, der erste Link in meinem ersten Absatz erwähnt dies bereits…

    – Bergi

    27. August 2015 um 20:38 Uhr

  • @user51462 “Schritt 36 initialisiert die in Schritt 10 identifizierten FDs” – das schließt aber keine Funktionsdeklarationen aus inneren Blöcken ein. Ihre Anhebung auf die Funktionsebene wird durch geregelt §B.3.3.1

    – Bergi

    29. Juni 2021 um 9:43 Uhr


  • @ user51462 Diese Liste von varDeclarations enthält keine Nicht-var Deklarationen aus Blöcken. Wenn ich das richtig verstehe, sogar geht direkt an TopLevelVarScopedDeclarations nur

    – Bergi

    29. Juni 2021 um 11:17 Uhr

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 fooS [[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.

  • Bist du dir da sicher? Ich denke strikt in B3.3 bezieht sich auf eine lokale Variable, die zuvor in der Algorithmusbeschreibung in eingeführt wurde 9.2.12. Und innerhalb dieses Algorithmus strict == func.[[Strict]].

    – rvidal

    15. Juli 2015 um 13:54 Uhr

  • @rvidal Ich verstehe deine Logik wirklich nicht. Bedenken Sie { var f, isStrict = IsInStrict(); if(!isStrict) { f = function() {'use strict'} } }. Nun, nehme an IsInStrict “funktioniert” (es gibt einige Vorschläge, wie man das auf SO testen kann), wenn nicht im strikten Modus, würde durch Ihre Logik, die in die if-Anweisung geht, der äußere Bereich in den strengen Modus versetzt. Das ist nicht logisch und erzeugt tatsächlich ein Paradoxon.

    – Amit

    15. Juli 2015 um 14:17 Uhr


  • @rvidal – Versuchen wir es noch einmal :). In Anhang B geht es um Legacy-Features, die NICHT Teil des Standards sind. Sie werden aufgeführt, um Verhalten zu beschreiben, das nicht dem Standard entspricht, aber erforderlich ist, um die Kompatibilität mit älterem Code aufrechtzuerhalten. B3.3 beschreiben eine solche Funktion, nämlich “Funktionsdeklarationen auf Blockebene”. Die zusätzlichen Schritte sind diejenigen, die bei Schritt 29 von 9.2.12 zu berücksichtigen sind und mit einer Bedingung beginnen, dass der relevante Block nicht im strikten Modus ist. In Ihrem Beispiel bezieht sich das auf eine in einem Block deklarierte Funktion, der Block ist tatsächlich nicht streng (da er nicht explizit streng ist) (Forts. als nächstes)

    – Amit

    15. Juli 2015 um 20:59 Uhr

  • Was folgt, sind eine Reihe von Bedingungen und Verhaltensweisen, die vom Standard abweichen und ein alternatives Verhalten definieren, dessen Zweck es ist, zu ermöglichen, dass der Legacy-Code so funktioniert, wie er ursprünglich entworfen wurde. Wenn Sie sich 9.2.12 Schritt 29 ansehen würden, würden Sie sehen, dass dieser Schritt auf B3.3 für Details im nicht strikten Modus verweist. Wenn sich Ihr ursprünglicher Block im Strict-Modus befand (wenn er beispielsweise mit „Use Strict“ beginnt), wird dieser gesamte Schritt übersprungen und das normale Standardverhalten angewendet. Ich hoffe, das ist etwas klarer, denn wenn nicht, glaube ich nicht, dass ich Ihnen eine zufriedenstellende Antwort geben kann (sorry dann 🙂

    – Amit

    15. Juli 2015 um 21:06 Uhr

  • @rvidal – Versuchen wir es mit einem demostrativen Weg :-). Schau dir das an Geige. Verwirrt Sie das mehr/weniger?

    – Amit

    16. Juli 2015 um 8:18 Uhr

964360cookie-checkWas ist die genaue Semantik von Funktionen auf Blockebene in ES6?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy