2206 Stimmen

Was ist der Geltungsbereich von Variablen in JavaScript?

Was ist der Umfang der Variablen in Javascript? Haben sie innerhalb und außerhalb einer Funktion den gleichen Geltungsbereich? Oder spielt das überhaupt eine Rolle? Und wo werden die Variablen gespeichert, wenn sie global definiert sind?

5 Stimmen

Hier ist eine weitere schöne Link dieses Thema im Auge zu behalten: " Erläuterung von JavaScript-Bereich und -Schließungen ".

10 Stimmen

Hier ist ein Artikel, der dies sehr gut erklärt. Alles, was Sie über den Umfang von Javascript-Variablen wissen müssen

3 Stimmen

Le site zuvor erwähnt E-Book von Kyle Simpson ist auf Github zu lesen, und es sagt Ihnen alles, was Sie über JavaScript Scopes & Closures wissen müssen. Sie können es hier finden: github.com/getify/You-Dont-Know-JS/blob/master/ Es ist Teil des Buchreihe "Du kennst JS nicht" die für alle geeignet ist, die mehr über JavaScript wissen möchten.

34voto

James McMahon Punkte 46733

Der Schlüssel, wie ich es verstehe, ist, dass Javascript Funktionsebene Scoping vs die häufigere C-Block Scoping hat.

Hier ist ein guter Artikel zu diesem Thema.

29voto

Travis J Punkte 78877

Die Idee des Scopings in JavaScript wurde ursprünglich von Brendan Eich stammt aus dem HyperCard Skriptsprache HyperTalk .

In dieser Sprache wurden die Anzeigen ähnlich wie ein Stapel von Karteikarten gestaltet. Es gab eine Hauptkarte, die als Hintergrund bezeichnet wurde. Sie war transparent und kann als unterste Karte gesehen werden. Jeder Inhalt auf dieser Grundkarte wurde mit den Karten, die darauf gelegt wurden, geteilt. Jede Karte, die darauf gelegt wurde, hatte ihren eigenen Inhalt, der Vorrang vor der vorherigen Karte hatte, aber immer noch Zugang zu den vorherigen Karten hatte, falls gewünscht.

Genau so ist das JavaScript-Scoping-System aufgebaut. Es hat nur andere Namen. Die Karten in JavaScript sind bekannt als Ausführungskontexte ECMA . Jeder dieser Kontexte besteht aus drei Hauptteilen. Eine variable Umgebung, eine lexikalische Umgebung und eine this-Bindung. Die lexikalische Umgebung enthält alle Inhalte der vorherigen Karten, die sich weiter unten im Stapel befinden. Der aktuelle Kontext steht ganz oben auf dem Stapel, und jeder dort deklarierte Inhalt wird in der variablen Umgebung gespeichert. Im Falle von Namenskollisionen hat die variable Umgebung Vorrang.

Diese Bindung verweist auf das enthaltende Objekt. Manchmal ändern sich Geltungsbereiche oder Ausführungskontexte, ohne dass sich das enthaltende Objekt ändert, wie z.B. in einer deklarierten Funktion, wo das enthaltende Objekt sein kann window oder eine Konstruktorfunktion.

Diese Ausführungskontexte werden bei jeder Übergabe der Kontrolle erstellt. Die Kontrolle wird übertragen, wenn die Ausführung des Codes beginnt, und dies geschieht hauptsächlich bei der Ausführung von Funktionen.

Das ist also die technische Erklärung. In der Praxis ist es wichtig zu wissen, dass in JavaScript

  • Scopes sind technisch gesehen "Ausführungskontexte".
  • Kontexte bilden einen Stapel von Umgebungen, in denen Variablen gespeichert werden
  • Der oberste Teil des Stapels hat Vorrang (der unterste Teil ist der globale Kontext)
  • Jede Funktion erzeugt einen Ausführungskontext (aber nicht immer eine neue Bindung)

Wenn man dies auf eines der vorherigen Beispiele (5. "Abschluss") auf dieser Seite anwendet, kann man den Stapel der Ausführungskontexte verfolgen. In diesem Beispiel befinden sich drei Kontexte auf dem Stapel. Sie sind definiert durch den äußeren Kontext, den Kontext in der unmittelbar von var six aufgerufenen Funktion und den Kontext in der zurückgegebenen Funktion innerhalb der unmittelbar aufgerufenen Funktion von var six.

i ) Der äußere Kontext. Er hat eine variable Umgebung von a = 1
ii ) Der IIFE-Kontext hat eine lexikalische Umgebung von a = 1, aber eine variable Umgebung von a = 6, die auf dem Stapel Vorrang hat
iii ) Der zurückgegebene Funktionskontext, er hat eine lexikalische Umgebung von a = 6 und das ist der Wert, auf den im Alert beim Aufruf verwiesen wird.

enter image description here

1 Stimmen

Wurde Javascript wirklich von Hypertalk inspiriert? Ich kann mich nicht erinnern, dass Hypertalk ein so interessantes Scoping hatte, aber die Inspiration würde vielleicht Javascript's bizarre Operatorüberladung erklären, wo 10=="10.0" und 10=="10", aber "10.0"!="10". Allerdings verhielten sich die Operatoren von Hypertalk noch interessanter.

1 Stimmen

@supercat - Jawohl. Ungefähr zu dieser Zeit hatte ich die Ursprünge des Internet Explorer (der auf Mosaic zurückgeht) erforscht, um herauszufinden, warum IE10 ein solches Sicherheitsproblem war, und hatte einen Teil dieser Forschung an Jonathan Sampson weitergeleitet. Wie es der Zufall so will, wurde kurz darauf Edge entwickelt, wobei viele der vorgeschlagenen Sicherheitsprobleme beseitigt wurden. Dieser Beitrag ist allerdings etwas veraltet, da die jüngste Iteration von EcmaScript und die Einbeziehung von Microtasks ein etwas komplizierteres Modell für die Speicherverwaltung hinter den Kulissen in bestimmten Szenarien geschaffen haben.

1 Stimmen

@supercat - Für einige noch verfügbare Referenzen dazu: "Ich fing an, mir Sprachen wie Logo und Smalltalk und Self und HyperTalk, Bill Atkinsons Sprache für HyperCard, anzusehen. Brendan Eich JavaScript (dessen Erfinder, Brendan Eich, von HyperTalk[32] inspiriert wurde)" - Wiki zitiert sein Buch . Hier ist die E-Mail, die ich an Jonathan von Microsoft geschrieben habe: jsfiddle.net/fwchpvrj

29voto

kennytm Punkte 488916

In "Javascript 1.7" (Mozilla's Erweiterung zu Javascript) kann man auch Block-Scope-Variablen deklarieren mit let Anweisung :

 var a = 4;
 let (a = 3) {
   alert(a); // 3
 }
 alert(a);   // 4

2 Stimmen

Ja, aber ist es sicher in der Anwendung? Ich meine, würde ich realistisch diese Implementierung wählen, wenn mein Code in WebKit laufen wird?

11 Stimmen

@Python: Nein, WebKit unterstützt nicht let .

0 Stimmen

Ich denke, die einzige sinnvolle Anwendung wäre, wenn Sie wüssten, dass alle Kunden einen Mozilla-Browser verwenden würden, wie z. B. bei einem internen System eines Unternehmens.

19voto

Gerard ONeill Punkte 3528

1) Es gibt einen globalen Bereich, einen Funktionsbereich und die Bereiche with und catch. Für Variablen gibt es im Allgemeinen keinen "Block"-Bereich - die with- und catch-Anweisungen fügen ihren Blöcken Namen hinzu.

2) Bereiche werden von Funktionen bis hin zum globalen Bereich verschachtelt.

3) Eigenschaften werden aufgelöst, indem die Prototypenkette durchlaufen wird. Die with-Anweisung bringt die Namen der Objekteigenschaften in den durch den with-Block definierten lexikalischen Bereich.

EDIT: ECMAAScript 6 (Harmony) ist spezifiziert, um Let zu unterstützen, und ich weiß, dass Chrome ein "Harmony"-Flag erlaubt, so dass es vielleicht doch unterstützt.

Let wäre eine Unterstützung für Block-Level-Scoping, aber Sie müssen das Schlüsselwort verwenden, um es zu ermöglichen.

EDIT: Aufgrund von Benjamins Hinweis auf die with- und catch-Anweisungen in den Kommentaren habe ich den Beitrag überarbeitet und weitere hinzugefügt. Sowohl die with- als auch die catch-Anweisungen führen Variablen in ihre jeweiligen Blöcke ein, und das ist einen Blockumfang. Diese Variablen sind mit den Eigenschaften der Objekte verknüpft, die an sie übergeben werden.

 //chrome (v8)

 var a = { 'test1':'test1val' }
 test1   // error not defined
 with (a) { var test1 = 'replaced' }
 test1   // undefined
 a       // a.test1 = 'replaced'

EDIT: Verdeutlichendes Beispiel:

Var test1' erzeugt eine neue Variable test1 im oberen lexikalischen Kontext (Funktion oder global), es sei denn, sie ist eine Eigenschaft von a - was sie ist.

Igitt! Seien Sie vorsichtig bei der Verwendung von "with" - genau wie var ein Noop ist, wenn die Variable bereits in der Funktion definiert ist, ist es auch ein Noop in Bezug auf Namen, die aus dem Objekt importiert werden! Eine kleine Vorwarnung, dass der Name bereits definiert ist, würde dies viel sicherer machen. Ich persönlich werde "with" aus diesem Grund nie verwenden.

0 Stimmen

Sie haben einige Fehler hier, zum einen JavaScript hat Formen von Block Scoping.

0 Stimmen

Meine Ohren (Augen) sind offen, Benjamin - Meine Aussagen oben sind, wie ich Javascript Scoping behandelt habe, aber sie sind nicht auf das Lesen der Spezifikation basiert. Und ich hoffe, du beziehst dich nicht auf die with-Anweisung (die eine Form des Objekt-Scopings ist) oder Mozillas spezielle 'let'-Syntax.

0 Stimmen

Nun, with Anweisung es eine Form des Block-Scopings, aber catch Klauseln sind eine viel häufigere Form (Spaß Tatsache, v8 implementiert catch mit einer with ) - das ist so ziemlich die einzige Form von Block-Scoping in JavaScript selbst (d.h. function, global, try/catch, with und ihre Derivate), aber Host-Umgebungen haben andere Vorstellungen von Scoping - zum Beispiel Inline-Events im Browser und NodeJS's vm-Modul.

14voto

CertainPerformance Punkte 306402

Inline-Handler

Ein sehr häufiges, noch nicht beschriebenes Problem, auf das Front-End-Programmierer oft stoßen, ist der Bereich, der für einen Inline-Event-Handler im HTML sichtbar ist - zum Beispiel mit

<button onclick="foo()"></button>

Der Geltungsbereich der Variablen, die eine on* Attribut kann referenzieren muss entweder sein:

  • global (funktionierende Inline-Handler verweisen fast immer auf globale Variablen)
  • eine Eigenschaft des Dokuments (z. B, querySelector als eigenständige Variable zeigt auf document.querySelector ; selten)
  • eine Eigenschaft des Elements, an das der Handler gebunden ist (wie oben; selten)

Andernfalls erhalten Sie einen ReferenceError, wenn der Handler aufgerufen wird. Wenn der Inline-Handler zum Beispiel auf eine Funktion verweist, die definiert ist innerhalb window.onload o $(function() { wird der Verweis fehlschlagen, da der Inline-Handler nur auf Variablen im globalen Bereich verweisen darf und die Funktion nicht global ist:

window.addEventListener('DOMContentLoaded', () => {
  function foo() {
    console.log('foo running');
  }
});

<button onclick="foo()">click</button>

Eigenschaften des document und Eigenschaften des Elements, an das der Handler angehängt ist, können auch als eigenständige Variablen innerhalb von Inline-Handlern referenziert werden, da Inline-Handler aufgerufen werden innerhalb von zwei with Blöcke eine für die document , eine für das Element. Die Geltungskette der Variablen innerhalb dieser Handler lautet extrem unintuitiv und ein funktionierender Event-Handler wird wahrscheinlich erfordern, dass eine Funktion global ist (und unnötige globale Verschmutzung sollte wahrscheinlich vermieden werden ).

Da die Scope-Kette innerhalb von Inline-Handlern so seltsam und da Inline-Handler globale Verschmutzung benötigen, um zu funktionieren, und da Inline-Handler manchmal hässliches String-Escaping bei der Übergabe von Argumenten benötigen, ist es wahrscheinlich einfacher, sie zu vermeiden. Fügen Sie stattdessen Event-Handler mit Javascript an (wie mit addEventListener ) und nicht mit HTML-Markup.

function foo() {
  console.log('foo running');
}
document.querySelector('.my-button').addEventListener('click', foo);

<button class="my-button">click</button>

Module ( <script type="module"> )

Im Gegensatz zu den normalen <script> Tags, die auf der obersten Ebene ausgeführt werden, läuft der Code in ES6-Modulen in seinem eigenen privaten Bereich. Eine Variable, die am Anfang eines normalen <script> Tag ist global, so dass Sie es in anderen Anwendungen referenzieren können. <script> Tags, etwa so:

<script>
const foo = 'foo';
</script>
<script>
console.log(foo);
</script>

Die oberste Ebene eines ES6-Moduls ist jedoch no global. Eine Variable, die am Anfang eines ES6-Moduls deklariert ist, ist nur innerhalb dieses Moduls sichtbar, es sei denn, die Variable ist explizit export oder wenn sie nicht einer Eigenschaft des globalen Objekts zugewiesen ist.

<script type="module">
const foo = 'foo';
</script>
<script>
// Can't access foo here, because the other script is a module
console.log(typeof foo);
</script>

Die oberste Ebene eines ES6-Moduls ist ähnlich wie das Innere eines IIFE auf der obersten Ebene in einer normalen <script> . Das Modul kann auf alle Variablen verweisen, die global sind, und nichts kann auf etwas innerhalb des Moduls verweisen, es sei denn, das Modul ist ausdrücklich dafür vorgesehen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X