Was ist eine kurze Einführung in das lexikalische Scoping?
javascript
scoping
lexical-scope
Subba Rao
quelle
quelle
Antworten:
Ich verstehe sie anhand von Beispielen. :) :)
Zunächst der lexikalische Bereich (auch als statischer Bereich bezeichnet ) in C-ähnlicher Syntax:
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:
Hier
fun
kann entweder aufx
indummy1
oderdummy2
oder auf einex
beliebige Funktion zugegriffen werden, diefun
mitx
deklariert aufgerufen wird.druckt 5,
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:
Die Aufrufkette hängt von einer Laufzeitbedingung ab. Wenn es wahr ist, sieht die Anrufkette wie folgt aus:
Wenn die Bedingung falsch ist:
Der äußere Bereich von ist
fun
in 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.
quelle
JavaScript
. Daher denke ich, dass dies nicht als akzeptierte Antwort markiert werden sollte. Lexikalischer Umfang speziell in JS ist andersfor
ist das typische Problem innerhalb einer Schleife. Der lexikalische Bereich für JavaScript befindet sich nur auf Funktionsebene, sofern nicht ES6let
oderconst
verwendet wird.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!
quelle
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..:)
quelle
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 .
quelle
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.
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.
quelle
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:
var _this = this; function callback(){ console.log(_this); }
callback.bind(this)
Ich denke, es ist erwähnenswert, dass JavaScript nicht wirklich über ein dynamisches Scoping verfügt .
.bind
Passt dasthis
Schlü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 Scoping
als Rückrufe in JavaScript bezeichnen:Gebunden
Eine andere Möglichkeit zum Umfang besteht darin, Folgendes zu verwenden
Function.prototype.bind
:Diese Methoden sind meines Wissens verhaltensmäßig gleichwertig.
quelle
bind
hat keinen Einfluss auf den Umfang.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.
quelle
IBM definiert es als:
Beispiel 1:
Beispiel 2:
quelle
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 .
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
likes
nicht 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 .
quelle
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:
// Ausgabe: JavaScript
quelle
bind
. Bei ihnen ist dasbind
nicht mehr erforderlich. Weitere Informationen zu dieser Änderung finden Sie unter stackoverflow.com/a/34361380/11127383Es 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:
quelle
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.
Ausgabe:
quelle
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.
Wenn Sie nun add () -> aufrufen, wird 3 gedruckt.
Die Funktion add () greift also auf die globale Variable zu,
x
die vor der Methodenfunktion add definiert wurde. Dies wird aufgrund des lexikalischen Bereichs in JavaScript aufgerufen.quelle
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.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.
foo
undbar
befinden sich immer im Lexikon der verfügbaren Bezeichner, da diese global sind.Wenn
function1
ausgeführt wird, hat es Zugriff auf ein Lexikonfoo2
,bar2
,foo
, undbar
.Wenn
function2
ausgeführt wird, hat es Zugriff auf ein Lexikonfoo3
,bar3
,foo2
,bar2
,foo
, undbar
.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.
quelle
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:
Status - nämlich Variablen und referenzierte Speicherorte auf dem Heap und Stack.
Operationen in diesem Zustand - nämlich jede Codezeile in Ihrem Programm
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.
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.
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.
quelle
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 dem
this
Objektreferenz.Um den Punkt zu veranschaulichen:
Im Beispiel ist die Variable
a
global definiert, jedoch in derdoit()
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
aardvark
nichtapple
welcher 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:
Dieses Codebeispiel führt jeweils eines aus. Sie können sehen, dass die Schaltfläche aufgrund des lexikalischen Gültigkeitsbereichs
A
die 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.
quelle
Normalerweise lerne ich anhand eines Beispiels und hier ist etwas:
quelle
Dieses Thema ist mit der eingebauten in engen Zusammenhang mit
bind
Funktion 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 wirbind
dies tun , um Zugriff auf den Bereich zu erhalten.JavaScript standardmäßig nicht ihren Anwendungsbereich
this
auf Funktionen (so dass er nicht den Kontext aufthis
). 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
this
die 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):
Mit Pfeilfunktionen (standardmäßig lexikalischer Bereich):
quelle