Was ist lexikalischer Umfang?


682

Was ist eine kurze Einführung in das lexikalische Scoping?


89
In Podcast 58 ermutigt Joel Fragen wie diese, da er möchte, dass SO DER Ort für Antworten wird, auch wenn sie an anderen Orten beantwortet wurden. Dies ist eine berechtigte Frage, auch wenn man es etwas höflicher formulieren könnte.
Ralph M. Rickenbach

5
@rahul Ich verstehe, es ist eine alte Frage. Aber ich bin mir sicher, dass SO auch 2009 erwartet hat, dass die Fragesteller grundlegende Anstrengungen unternehmen, um das Problem zu lösen. So wie es aussieht, zeigt es überhaupt keine Anstrengung. Vielleicht wurde es deshalb von vielen abgelehnt?
PP

13
Es ist möglich, dass der Fragesteller beim Schreiben dieser Frage nicht fließend Englisch spricht (oder nicht sprach)
Martin

27
Die Frage ist höflich, er sagt nur, was er will. Sie können frei antworten. Keine Notwendigkeit für überempfindliche Höflichkeit hier.
Markus Siebeneicher

25
Ich finde Fragen wie diese großartig, weil sie Inhalte für SO erstellen. IMO, wen interessiert es, wenn die Frage keine Mühe hat ... die Antworten werden großartigen Inhalt haben und darauf kommt es in diesem Message Board an.
Jwan622

Antworten:


685

Ich verstehe sie anhand von Beispielen. :) :)

Zunächst der lexikalische Bereich (auch als statischer Bereich bezeichnet ) in C-ähnlicher Syntax:

void fun()
{
    int x = 5;

    void fun2()
    {
        printf("%d", x);
    }
}

Jede innere Ebene kann auf ihre äußeren Ebenen zugreifen.

Es gibt einen anderen Weg, der als dynamischer Bereich bezeichnet wird und von der ersten Implementierung von Lisp verwendet wird , wiederum in einer C-ähnlichen Syntax:

void fun()
{
    printf("%d", x);
}

void dummy1()
{
    int x = 5;

    fun();
}

void dummy2()
{
    int x = 10;

    fun();
}

Hier funkann entweder auf xin dummy1oder dummy2oder auf eine xbeliebige Funktion zugegriffen werden, die funmit xdeklariert aufgerufen wird.

dummy1();

druckt 5,

dummy2();

druckt 10.

Der erste wird als statisch bezeichnet, da er zur Kompilierungszeit abgeleitet werden kann, und der zweite wird als dynamisch bezeichnet, da der äußere Bereich dynamisch ist und vom Kettenaufruf der Funktionen abhängt.

Ich finde statisches Scoping für das Auge einfacher. Die meisten Sprachen gingen diesen Weg schließlich, sogar Lisp (kann beides, oder?). Dynamisches Scoping ist wie das Übergeben von Referenzen aller Variablen an die aufgerufene Funktion.

Als Beispiel dafür, warum der Compiler den äußeren dynamischen Bereich einer Funktion nicht ableiten kann, betrachten wir unser letztes Beispiel. Wenn wir so etwas schreiben:

if(/* some condition */)
    dummy1();
else
    dummy2();

Die Aufrufkette hängt von einer Laufzeitbedingung ab. Wenn es wahr ist, sieht die Anrufkette wie folgt aus:

dummy1 --> fun()

Wenn die Bedingung falsch ist:

dummy2 --> fun()

Der äußere Bereich von ist funin beiden Fällen der Anrufer plus der Anrufer des Anrufers und so weiter .

Nur um zu erwähnen, dass die C-Sprache weder verschachtelte Funktionen noch dynamisches Scoping zulässt.


19
Ich möchte auch auf ein sehr, sehr leicht verständliches Tutorial hinweisen, das ich gerade gefunden habe. Das Beispiel von Arak ist nett, aber möglicherweise zu kurz für jemanden, der mehr Beispiele benötigt (im Vergleich zu anderen Sprachen ..). Schau mal. Es ist wichtig, dies zu verstehen , da dieses Schlüsselwort dazu führt, dass wir den lexikalischen Umfang verstehen. howtonode.org/what-is-this
CppLearner

9
Das ist eine gute Antwort. Aber die Frage ist markiert mit JavaScript. Daher denke ich, dass dies nicht als akzeptierte Antwort markiert werden sollte. Lexikalischer Umfang speziell in JS ist anders
Boyang

6
Sehr gute Antwort. Vielen Dank. @ Boyang Ich bin anderer Meinung. Ich bin kein Lisp-Codierer, fand aber das Lisp-Beispiel hilfreich, da es ein Beispiel für dynamisches Scoping ist, das Sie in JS nicht erhalten.
Dudewad

4
Anfangs dachte ich, das Beispiel sei gültiger C-Code und war verwirrt, ob es in C ein dynamisches Scoping gibt. Vielleicht könnte der Haftungsausschluss am Ende vor dem Codebeispiel verschoben werden?
Yangshun Tay

2
Dies ist immer noch eine sehr hilfreiche Antwort, aber ich denke, @Boyang ist richtig. Diese Antwort bezieht sich auf "Ebene", was eher dem Blockumfang von C entspricht. JavaScript hat standardmäßig keinen Bereich auf Blockebene, daher forist das typische Problem innerhalb einer Schleife. Der lexikalische Bereich für JavaScript befindet sich nur auf Funktionsebene, sofern nicht ES6 letoder constverwendet wird.
icc97

274

Versuchen wir die kürzestmögliche Definition:

Lexikalisches Scoping definiert, wie Variablennamen in verschachtelten Funktionen aufgelöst werden: Innere Funktionen enthalten den Umfang der übergeordneten Funktionen, auch wenn die übergeordnete Funktion zurückgegeben wurde .

Das ist alles was dazu gehört!


21
Der letzte Teil: "Auch wenn die übergeordnete Funktion zurückgegeben wurde" heißt Closure.
Juanma Menendez

1
Verstand das Lexical Scoping & Closure in nur einem Gefühl. Vielen Dank!!
Dungeon

63
var scope = "I am global";
function whatismyscope(){
   var scope = "I am just a local";
   function func() {return scope;}
   return func;
}

whatismyscope()()

Der obige Code gibt "Ich bin nur ein Einheimischer" zurück. Es wird nicht "Ich bin ein Global" zurückgegeben. Weil die Funktion func () zählt, wo ursprünglich definiert wurde, was unter den Funktionsumfang whatismyscope fällt.

Es wird sich nicht darum kümmern, was auch immer es genannt wird (der globale Bereich / sogar innerhalb einer anderen Funktion), deshalb wird der globale Bereichswert, den ich global bin, nicht gedruckt.

Dies wird als lexikalisches Scoping bezeichnet, bei dem " Funktionen mithilfe der Gültigkeitsbereichskette ausgeführt werden, die zum Zeitpunkt der Definition gültig war " - gemäß JavaScript Definition Guide.

Lexikalischer Bereich ist ein sehr sehr leistungsfähiges Konzept.

Hoffe das hilft..:)


3
Es ist eine sehr schöne Erklärung. Ich möchte noch etwas hinzufügen, wenn Sie die Funktion func () {return this.scope;} schreiben. Dann wird "Ich bin global" zurückgegeben. Verwenden Sie einfach dieses Schlüsselwort und Ihr Bereich wird sich ändern
Rajesh Kumar Bhawsar

41

Lexikalisches (statisches AKA) Scoping bezieht sich auf die Bestimmung des Gültigkeitsbereichs einer Variablen ausschließlich anhand ihrer Position innerhalb des Textcode-Korpus. Eine Variable bezieht sich immer auf ihre Umgebung der obersten Ebene. Es ist gut, es in Bezug auf den dynamischen Umfang zu verstehen .


41

Der Bereich definiert den Bereich, in dem Funktionen, Variablen und dergleichen verfügbar sind. Die Verfügbarkeit einer Variablen wird beispielsweise im Kontext definiert, beispielsweise in der Funktion, Datei oder dem Objekt, in dem sie definiert sind. Wir nennen diese lokalen Variablen normalerweise.

Der lexikalische Teil bedeutet, dass Sie den Umfang aus dem Lesen des Quellcodes ableiten können.

Der lexikalische Bereich wird auch als statischer Bereich bezeichnet.

Der dynamische Bereich definiert globale Variablen, die nach der Definition von überall aufgerufen oder referenziert werden können. Manchmal werden sie als globale Variablen bezeichnet, obwohl globale Variablen in den meisten Programmiersprachen lexikalisch sind. Dies bedeutet, dass aus dem Lesen des Codes abgeleitet werden kann, dass die Variable in diesem Kontext verfügbar ist. Möglicherweise muss man einer Verwendungs- oder Includes-Klausel folgen, um die Instanziierung oder Definition zu finden, aber der Code / Compiler kennt die Variable an dieser Stelle.

Im Gegensatz dazu suchen Sie beim dynamischen Scoping zuerst in der lokalen Funktion, dann in der Funktion, die die lokale Funktion aufgerufen hat, dann in der Funktion, die diese Funktion aufgerufen hat, und so weiter im Aufrufstapel. "Dynamisch" bezieht sich auf eine Änderung, da der Aufrufstapel bei jedem Aufruf einer bestimmten Funktion unterschiedlich sein kann und die Funktion daher je nach Aufruf möglicherweise unterschiedliche Variablen trifft. (siehe hier )

Ein interessantes Beispiel für den dynamischen Bereich finden Sie hier .

Weitere Details finden Sie hier und hier .

Einige Beispiele in Delphi / Object Pascal

Delphi hat einen lexikalischen Geltungsbereich.

unit Main;
uses aUnit;  // makes available all variables in interface section of aUnit

interface

  var aGlobal: string; // global in the scope of all units that use Main;
  type 
    TmyClass = class
      strict private aPrivateVar: Integer; // only known by objects of this class type
                                    // lexical: within class definition, 
                                    // reserved word private   
      public aPublicVar: double;    // known to everyboday that has access to a 
                                    // object of this class type
    end;

implementation

  var aLocalGlobal: string; // known to all functions following 
                            // the definition in this unit    

end.

Das Delphi, das dem dynamischen Bereich am nächsten kommt, ist das Funktionspaar RegisterClass () / GetClass (). Zur Verwendung siehe hier .

Angenommen, die Zeit, zu der RegisterClass ([TmyClass]) aufgerufen wird, um eine bestimmte Klasse zu registrieren, kann nicht durch Lesen des Codes vorhergesagt werden (dies wird in einer vom Benutzer aufgerufenen Button-Click-Methode aufgerufen). Der Code, der GetClass ('TmyClass') aufruft, wird abgerufen ein Ergebnis oder nicht. Der Aufruf von RegisterClass () muss mit GetClass () nicht im lexikalischen Bereich der Einheit liegen.

Eine weitere Möglichkeit für den dynamischen Bereich sind anonyme Methoden (Closures) in Delphi 2009, da sie die Variablen ihrer aufrufenden Funktion kennen. Es folgt dem aufrufenden Pfad von dort nicht rekursiv und ist daher nicht vollständig dynamisch.


2
Tatsächlich ist privat in der gesamten Einheit zugänglich, in der die Klasse definiert ist. Aus diesem Grund wurde "Strict Private" in D2006 eingeführt.
Marco van de Voort

2
+1 für Klartext (im Gegensatz zu komplizierter Sprache und Beispielen ohne viel Beschreibung)
Pops

36

Ich liebe die voll funktionsfähigen, sprachunabhängigen Antworten von Leuten wie @Arak. Da diese Frage mit JavaScript getaggt wurde , möchte ich einige Notizen einfügen, die für diese Sprache sehr spezifisch sind.

In JavaScript haben wir folgende Möglichkeiten für das Scoping:

  • wie besehen (keine Bereichsanpassung)
  • lexikalisch var _this = this; function callback(){ console.log(_this); }
  • gebunden callback.bind(this)

Ich denke, es ist erwähnenswert, dass JavaScript nicht wirklich über ein dynamisches Scoping verfügt . .bindPasst das thisSchlüsselwort an, und das ist nah, aber technisch nicht dasselbe.

Hier ist ein Beispiel, das beide Ansätze demonstriert. Sie tun dies jedes Mal, wenn Sie eine Entscheidung darüber treffen, wie Rückrufe behandelt werden sollen. Dies gilt also für Versprechen, Ereignishandler und mehr.

Lexikalisch

Folgendes können Sie Lexical Scopingals Rückrufe in JavaScript bezeichnen:

var downloadManager = {
  initialize: function() {
    var _this = this; // Set up `_this` for lexical access
    $('.downloadLink').on('click', function () {
      _this.startDownload();
    });
  },
  startDownload: function(){
    this.thinking = true;
    // Request the file from the server and bind more callbacks for when it returns success or failure
  }
  //...
};

Gebunden

Eine andere Möglichkeit zum Umfang besteht darin, Folgendes zu verwenden Function.prototype.bind:

var downloadManager = {
  initialize: function() {
    $('.downloadLink').on('click', function () {
      this.startDownload();
    }.bind(this)); // Create a function object bound to `this`
  }
//...

Diese Methoden sind meines Wissens verhaltensmäßig gleichwertig.


Die Verwendung bindhat keinen Einfluss auf den Umfang.
Ben Aston

12

Lexikalisches Scoping: Variablen, die außerhalb einer Funktion deklariert wurden, sind globale Variablen und überall in einem JavaScript-Programm sichtbar. Innerhalb einer Funktion deklarierte Variablen haben einen Funktionsumfang und sind nur für Code sichtbar, der in dieser Funktion angezeigt wird.


12

IBM definiert es als:

Der Teil eines Programms oder einer Segmenteinheit, in dem eine Deklaration gilt. Ein in einer Routine deklarierter Bezeichner ist innerhalb dieser Routine und in allen verschachtelten Routinen bekannt. Wenn eine verschachtelte Routine ein Element mit demselben Namen deklariert, ist das äußere Element in der verschachtelten Routine nicht verfügbar.

Beispiel 1:

function x() {
    /*
    Variable 'a' is only available to function 'x' and function 'y'.
    In other words the area defined by 'x' is the lexical scope of
    variable 'a'
    */
    var a = "I am a";

    function y() {
        console.log( a )
    }
    y();

}
// outputs 'I am a'
x();

Beispiel 2:

function x() {

    var a = "I am a";

    function y() {
         /*
         If a nested routine declares an item with the same name,
         the outer item is not available in the nested routine.
         */
        var a = 'I am inner a';
        console.log( a )
    }
    y();

}
// outputs 'I am inner a'
x();

8

Lexikalischer Bereich bedeutet, dass in einer verschachtelten Gruppe von Funktionen die inneren Funktionen Zugriff auf die Variablen und andere Ressourcen ihres übergeordneten Bereichs haben . Dies bedeutet, dass die untergeordneten Funktionen lexikalisch an den Ausführungskontext ihrer Eltern gebunden sind. Der lexikalische Bereich wird manchmal auch als statischer Bereich bezeichnet .

function grandfather() {
    var name = 'Hammad';
    // 'likes' is not accessible here
    function parent() {
        // 'name' is accessible here
        // 'likes' is not accessible here
        function child() {
            // Innermost level of the scope chain
            // 'name' is also accessible here
            var likes = 'Coding';
        }
    }
}

Was Sie am lexikalischen Bereich bemerken werden, ist, dass es vorwärts funktioniert, was bedeutet, dass auf den Namen von den Ausführungskontexten seiner Kinder zugegriffen werden kann. Es funktioniert jedoch nicht rückwärts für die Eltern, was bedeutet, dass die Eltern likesnicht auf die Variable zugreifen können.

Dies zeigt uns auch, dass Variablen mit demselben Namen in verschiedenen Ausführungskontexten von oben nach unten im Ausführungsstapel Vorrang haben. Eine Variable mit einem ähnlichen Namen wie eine andere Variable in der innersten Funktion (oberster Kontext des Ausführungsstapels) hat eine höhere Priorität.

Beachten Sie, dass dies von hier übernommen wird .


8

In einfacher Sprache ist der lexikalische Bereich eine Variable, die außerhalb Ihres Bereichs definiert ist, oder der obere Bereich ist automatisch innerhalb Ihres Bereichs verfügbar, was bedeutet, dass Sie ihn dort nicht übergeben müssen.

Beispiel:

let str="JavaScript";

const myFun = () => {
    console.log(str);
}

myFun();

// Ausgabe: JavaScript


2
Die kürzeste und beste Antwort für mich mit einem Beispiel. Könnte hinzugefügt worden sein, dass die Pfeilfunktionen des ES6 das Problem mit lösen bind. Bei ihnen ist das bindnicht mehr erforderlich. Weitere Informationen zu dieser Änderung finden Sie unter stackoverflow.com/a/34361380/11127383
Daniel Danielecki

4

Es fehlt ein wichtiger Teil der Konversation über das lexikalische und dynamische Scoping : eine einfache Erklärung der Lebensdauer der Scoping-Variablen - oder wann auf die Variable zugegriffen werden kann.

Dynamisches Scoping entspricht nur sehr lose dem "globalen" Scoping, wie wir es traditionell betrachten (der Grund, warum ich den Vergleich zwischen beiden anspreche, ist, dass es bereits erwähnt wurde - und ich mag die Erklärung des verlinkten Artikels nicht besonders ); Es ist wahrscheinlich am besten, wenn wir keinen Vergleich zwischen global und dynamisch anstellen - obwohl laut dem verlinkten Artikel "... [es] als Ersatz für Variablen mit globalem Gültigkeitsbereich nützlich sein soll".

Was ist also im Klartext der wichtige Unterschied zwischen den beiden Scoping-Mechanismen?

Das lexikalische Scoping wurde in den obigen Antworten sehr gut definiert: Variablen mit lexikalischem Scoping sind auf der lokalen Ebene der Funktion, in der sie definiert wurden, verfügbar oder zugänglich.

Da es jedoch nicht im Mittelpunkt des OP steht, hat das dynamische Scoping nicht viel Aufmerksamkeit erhalten, und die Aufmerksamkeit, die es erhalten hat, bedeutet, dass es wahrscheinlich etwas mehr braucht (das ist keine Kritik an anderen Antworten, sondern eher ein "oh, Diese Antwort ließ uns wünschen, es gäbe ein bisschen mehr "). Also, hier ist ein bisschen mehr:

Dynamisches Scoping bedeutet, dass eine Variable während der Lebensdauer des Funktionsaufrufs oder während der Ausführung der Funktion für das größere Programm zugänglich ist. Wirklich, Wikipedia macht einen guten Job mit der Erklärung des Unterschieds zwischen den beiden. Um dies nicht zu verschleiern, finden Sie hier den Text, der das dynamische Scoping beschreibt:

... [I] In einem dynamischen Bereich (oder einem dynamischen Bereich) ist der Bereich eines Variablennamens eine bestimmte Funktion, und sein Bereich ist der Zeitraum, in dem die Funktion ausgeführt wird: Während die Funktion ausgeführt wird, existiert der Variablenname und ist an seine Variable gebunden, aber nachdem die Funktion zurückgegeben wurde, existiert der Variablenname nicht.


3

Lexikalischer Bereich bedeutet, dass eine Funktion Variablen in dem Kontext sucht, in dem sie definiert wurde, und nicht in dem Bereich, der sie unmittelbar umgibt.

Sehen Sie sich an, wie der lexikalische Bereich in Lisp funktioniert wenn Sie weitere Details wünschen. Die ausgewählte Antwort von Kyle Cronin in Dynamische und Lexikalische Variablen in Common Lisp ist viel klarer als die Antworten hier.

Zufälligerweise habe ich dies nur in einer Lisp-Klasse erfahren, und es gilt zufällig auch in JavaScript.

Ich habe diesen Code in der Chrome-Konsole ausgeführt.

// JavaScript               Equivalent Lisp
var x = 5;                //(setf x 5)
console.debug(x);         //(print x)
function print_x(){       //(defun print-x ()
    console.debug(x);     //    (print x)
}                         //)
(function(){              //(let
    var x = 10;           //    ((x 10))
    console.debug(x);     //    (print x)
    print_x();            //    (print-x)
})();                     //)

Ausgabe:

5
10
5

3

Ein lexikalischer Bereich in JavaScript bedeutet, dass auf eine außerhalb einer Funktion definierte Variable innerhalb einer anderen nach der Variablendeklaration definierten Funktion zugegriffen werden kann. Das Gegenteil ist jedoch nicht der Fall. Auf die innerhalb einer Funktion definierten Variablen kann außerhalb dieser Funktion nicht zugegriffen werden.

Dieses Konzept wird häufig in Abschlüssen in JavaScript verwendet.

Nehmen wir an, wir haben den folgenden Code.

var x = 2;
var add = function() {
    var y = 1;
    return x + y;
};

Wenn Sie nun add () -> aufrufen, wird 3 gedruckt.

Die Funktion add () greift also auf die globale Variable zu, xdie vor der Methodenfunktion add definiert wurde. Dies wird aufgrund des lexikalischen Bereichs in JavaScript aufgerufen.


Beachten Sie, dass das Code-Snippet für eine Sprache mit dynamischem Gültigkeitsbereich gedacht war. Wenn die add()Funktion unmittelbar nach dem angegebenen Codeausschnitt aufgerufen wird, wird auch 3 ausgegeben. Lexikalisches Scoping bedeutet nicht einfach, dass eine Funktion auf globale Variablen außerhalb des lokalen Kontexts zugreifen kann. Der Beispielcode hilft also nicht wirklich zu zeigen, was lexikalisches Scoping bedeutet. Das Anzeigen des lexikalischen Bereichs im Code erfordert wirklich ein Gegenbeispiel oder zumindest eine Erklärung anderer möglicher Interpretationen des Codes.
C Perkins

2

Der lexikalische Bereich bezieht sich auf das Lexikon von Bezeichnern (z. B. Variablen, Funktionen usw.), die von der aktuellen Position im Ausführungsstapel aus sichtbar sind.

- global execution context
    - foo
    - bar
    - function1 execution context
        - foo2
        - bar2
        - function2 execution context
            - foo3
            - bar3

foo und bar befinden sich immer im Lexikon der verfügbaren Bezeichner, da diese global sind.

Wenn function1ausgeführt wird, hat es Zugriff auf ein Lexikon foo2, bar2, foo, und bar.

Wenn function2ausgeführt wird, hat es Zugriff auf ein Lexikon foo3, bar3, foo2, bar2, foo, und bar.

Der Grund, warum globale und / oder äußere Funktionen keinen Zugriff auf Bezeichner für innere Funktionen haben, liegt darin, dass die Ausführung dieser Funktion noch nicht erfolgt ist und daher keiner ihrer Bezeichner dem Speicher zugewiesen wurde. Sobald dieser innere Kontext ausgeführt wird, wird er aus dem Ausführungsstapel entfernt, was bedeutet, dass alle seine Bezeichner durch Müll gesammelt wurden und nicht mehr verfügbar sind.

Aus diesem Grund kann ein verschachtelter Ausführungskontext IMMER auf den Ausführungskontext seiner Vorfahren zugreifen und hat daher Zugriff auf ein größeres Lexikon von Bezeichnern.

Sehen:

Besonderer Dank geht an @ robr3rd für die Hilfe bei der Vereinfachung der obigen Definition.


1

Hier ist ein anderer Blickwinkel auf diese Frage, den wir erhalten können, wenn wir einen Schritt zurücktreten und die Rolle des Scoping im größeren Interpretationsrahmen (Ausführen eines Programms) betrachten. Mit anderen Worten, stellen Sie sich vor, Sie hätten einen Interpreter (oder Compiler) für eine Sprache erstellt und wären für die Berechnung der Ausgabe verantwortlich, wenn Sie ein Programm und einige Eingaben dafür gegeben hätten.

Bei der Interpretation müssen drei Dinge im Auge behalten werden:

  1. Status - nämlich Variablen und referenzierte Speicherorte auf dem Heap und Stack.

  2. Operationen in diesem Zustand - nämlich jede Codezeile in Ihrem Programm

  3. Die Umgebung, in der eine bestimmte Operation ausgeführt wird - nämlich die Projektion des Zustands auf eine Operation.

Ein Interpreter beginnt in der ersten Codezeile eines Programms, berechnet seine Umgebung, führt die Zeile in dieser Umgebung aus und erfasst deren Auswirkungen auf den Programmstatus. Es folgt dann dem Kontrollfluss des Programms, um die nächste Codezeile auszuführen, und wiederholt den Vorgang, bis das Programm endet.

Die Art und Weise, wie Sie die Umgebung für eine Operation berechnen, erfolgt über ein formales Regelwerk, das von der Programmiersprache definiert wird. Der Begriff "Bindung" wird häufig verwendet, um die Zuordnung des Gesamtzustands des Programms zu einem Wert in der Umgebung zu beschreiben. Beachten Sie, dass mit "Gesamtzustand" nicht der globale Zustand gemeint ist, sondern die Gesamtsumme jeder erreichbaren Definition zu jedem Zeitpunkt der Ausführung.

Dies ist der Rahmen, in dem das Scoping-Problem definiert wird. Nun zum nächsten Teil unserer Optionen.

  • Als Implementierer des Interpreters können Sie Ihre Aufgabe vereinfachen, indem Sie die Umgebung so nah wie möglich am Programmstatus gestalten. Dementsprechend würde die Umgebung einer Codezeile einfach durch die Umgebung der vorherigen Codezeile definiert, wobei die Auswirkungen dieser Operation auf sie angewendet würden, unabhängig davon, ob die vorherige Zeile eine Zuweisung, ein Funktionsaufruf, eine Rückkehr von einer Funktion war. oder eine Steuerstruktur wie eine while-Schleife.

Dies ist der Kern des dynamischen Scoping , bei dem die Umgebung, in der Code ausgeführt wird, an den Status des Programms gebunden ist, der durch seinen Ausführungskontext definiert ist.

  • Oder , halten Sie von einem Programmierer könnte Ihre Sprache verwenden und vereinfachen seine Aufgabe den Überblick über die Werte halten eine Variable annehmen kann. Es gibt viel zu viele Wege und zu viel Komplexität, um über das Ergebnis der Gesamtheit der vergangenen Ausführung nachzudenken. Lexical Scoping hilft dabei, indem es die aktuelle Umgebung auf den Teil des Status beschränkt , der im aktuellen Block, in der Funktion oder in einer anderen Bereichseinheit definiert ist, sowie auf dessen übergeordneten Block (dh den Block, der die aktuelle Uhr enthält, oder die Funktion, die die aktuelle Funktion aufgerufen hat).

Mit anderen Worten, mit lexikalischem Bereich ist die Umgebung, die jeder Code sieht, an den Status gebunden, der einem Bereich zugeordnet ist, der explizit in der Sprache definiert ist, z. B. ein Block oder eine Funktion.


0

Alte Frage, aber hier ist meine Einstellung dazu.

Der lexikalische (statische) Bereich bezieht sich auf den Bereich einer Variablen im Quellcode .

In einer Sprache wie JavaScript, in der Funktionen weitergegeben und an verschiedene Objekte angehängt und wieder angehängt werden können, hängt dieser Bereich möglicherweise davon ab, wer die Funktion gerade aufruft, dies ist jedoch nicht der Fall. Das Ändern des Bereichs auf diese Weise wäre ein dynamischer Bereich, und JavaScript tut dies nicht, außer möglicherweise mit demthis Objektreferenz.

Um den Punkt zu veranschaulichen:

var a='apple';

function doit() {
    var a='aardvark';
    return function() {
        alert(a);
    }
}

var test=doit();
test();

Im Beispiel ist die Variable aglobal definiert, jedoch in der doit()Funktion schattiert . Diese Funktion gibt eine andere Funktion zurück, die, wie Sie sehen, auf der basierta Variablen außerhalb ihres eigenen Bereichs .

Wenn Sie dies ausführen, werden Sie feststellen, dass der verwendete Wert aardvarknicht applewelcher ist, obwohl er im Bereich von liegttest() Funktion, ist nicht in dem lexikalischen Umfang der ursprünglichen Funktion. Das heißt, der verwendete Bereich ist der Bereich, wie er im Quellcode angezeigt wird, nicht der Bereich, in dem die Funktion tatsächlich verwendet wird.

Diese Tatsache kann ärgerliche Folgen haben. Sie können beispielsweise entscheiden, dass es einfacher ist, Ihre Funktionen separat zu organisieren und sie dann zu gegebener Zeit zu verwenden, z. B. in einem Ereignishandler:

var a='apple',b='banana';

function init() {
  var a='aardvark',b='bandicoot';
  document.querySelector('button#a').onclick=function(event) {
    alert(a);
  }
  document.querySelector('button#b').onclick=doB;
}

function doB(event) {
  alert(b);
}

init();
<button id="a">A</button>
<button id="b">B</button>

Dieses Codebeispiel führt jeweils eines aus. Sie können sehen, dass die Schaltfläche aufgrund des lexikalischen Gültigkeitsbereichs Adie innere Variable while-Schaltfläche verwendetB dies nicht tut. Möglicherweise erhalten Sie mehr Verschachtelungsfunktionen, als Sie gerne hätten.

Übrigens werden Sie in beiden Beispielen auch feststellen, dass die Variablen mit dem inneren lexikalischen Gültigkeitsbereich bestehen bleiben, obwohl die enthaltende Funktionsfunktion ihren Lauf genommen hat. Dies wird als Abschluss bezeichnet und bezieht sich auf den Zugriff einer verschachtelten Funktion auf äußere Variablen, selbst wenn die äußere Funktion beendet wurde. JavaScript muss intelligent genug sein, um festzustellen, ob diese Variablen nicht mehr benötigt werden, und wenn nicht, kann sie durch Müll gesammelt werden.


-1

Normalerweise lerne ich anhand eines Beispiels und hier ist etwas:

const lives = 0;

function catCircus () {
    this.lives = 1;
    const lives = 2;

    const cat1 = {
        lives: 5,
        jumps: () => {
            console.log(this.lives);
        }
    };
    cat1.jumps(); // 1
    console.log(cat1); // { lives: 5, jumps: [Function: jumps] }

    const cat2 = {
        lives: 5,
        jumps: () => {
            console.log(lives);
        }
    };
    cat2.jumps(); // 2
    console.log(cat2); // { lives: 5, jumps: [Function: jumps] }

    const cat3 = {
        lives: 5,
        jumps: () => {
            const lives = 3;
            console.log(lives);
        }
    };
    cat3.jumps(); // 3
    console.log(cat3); // { lives: 5, jumps: [Function: jumps] }

    const cat4 = {
        lives: 5,
        jumps: function () {
            console.log(lives);
        }
    };
    cat4.jumps(); // 2
    console.log(cat4); // { lives: 5, jumps: [Function: jumps] }

    const cat5 = {
        lives: 5,
        jumps: function () {
            var lives = 4;
            console.log(lives);
        }
    };
    cat5.jumps(); // 4
    console.log(cat5); // { lives: 5, jumps: [Function: jumps] }

    const cat6 = {
        lives: 5,
        jumps: function () {
            console.log(this.lives);
        }
    };
    cat6.jumps(); // 5
    console.log(cat6); // { lives: 5, jumps: [Function: jumps] }

    const cat7 = {
        lives: 5,
        jumps: function thrownOutOfWindow () {
            console.log(this.lives);
        }
    };
    cat7.jumps(); // 5
    console.log(cat7); // { lives: 5, jumps: [Function: thrownOutOfWindow] }
}

catCircus();

-1

Dieses Thema ist mit der eingebauten in engen Zusammenhang mit bindFunktion und eingeführt in ECMAScript 6 Pfeil Funktionen . Es war wirklich ärgerlich, denn für jede neue "Klasse" -Methode (Funktion tatsächlich), die wir verwenden wollten, mussten wir binddies tun , um Zugriff auf den Bereich zu erhalten.

JavaScript standardmäßig nicht ihren Anwendungsbereich thisauf Funktionen (so dass er nicht den Kontext auf this). Standardmäßig müssen Sie explizit angeben, welchen Kontext Sie haben möchten.

Die Pfeilfunktionen erhalten automatisch einen sogenannten lexikalischen Bereich (Zugriff auf die Variablendefinition in ihrem enthaltenen Block). Beim Benutzen Pfeilfunktionen wird automatisch an thisdie Stelle gebunden, an der die Pfeilfunktion ursprünglich definiert wurde, und der Kontext dieser Pfeilfunktionen ist der enthaltende Block.

Sehen Sie anhand der einfachsten Beispiele unten, wie es in der Praxis funktioniert.

Vor Pfeilfunktionen (standardmäßig kein lexikalischer Bereich):

const programming = {
  language: "JavaScript",
  getLanguage: function() {
    return this.language;
  }
}

const globalScope = programming.getLanguage;
console.log(globalScope()); // Output: undefined

const localScope = programming.getLanguage.bind(programming);
console.log(localScope()); // Output: "JavaScript"

Mit Pfeilfunktionen (standardmäßig lexikalischer Bereich):

const programming = {
  language: "JavaScript",
  getLanguage: function() {
    return this.language;
  }
}

const arrowFunction = () => {
    console.log(programming.getLanguage());
}

arrowFunction(); // Output: "JavaScript"
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.