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.

2681voto

Kenan Banks Punkte 196831

TLDR

JavaScript verfügt über lexikalisches (auch statisches) Scoping und Closures. Das bedeutet, dass Sie den Geltungsbereich eines Bezeichners erkennen können, indem Sie sich den Quellcode ansehen.

Die vier Geltungsbereiche sind:

  1. Global - für alle sichtbar
  2. Funktion - sichtbar innerhalb einer Funktion (und ihrer Unterfunktionen und Blöcke)
  3. Block - sichtbar innerhalb eines Blocks (und dessen Unterblöcken)
  4. Modul - sichtbar innerhalb eines Moduls

Außerhalb der Sonderfälle des globalen und des Modulbereichs werden Variablen mit var (Funktionsumfang), let (Blockumfang), und const (Blockumfang). Die meisten anderen Formen der Bezeichnerdeklaration haben im Strict-Modus einen Blockbereich.

Übersicht

Der Geltungsbereich ist der Bereich der Codebasis, in dem ein Bezeichner gültig ist.

Eine lexikalische Umgebung ist eine Abbildung zwischen Bezeichnernamen und den mit ihnen verbundenen Werten.

Der Umfang besteht aus einer verknüpften Verschachtelung von lexikalischen Umgebungen, wobei jede Ebene der Verschachtelung einer lexikalischen Umgebung eines Vorgänger-Ausführungskontexts entspricht.

Diese verknüpften lexikalischen Umgebungen bilden eine "Scope-Kette". Die Bezeichnerauflösung ist der Prozess der Suche nach einem passenden Bezeichner entlang dieser Kette.

Die Auflösung von Identifikatoren erfolgt nur in einer Richtung: nach außen. Auf diese Weise können äußere lexikalische Umgebungen nicht in innere lexikalische Umgebungen "hineinsehen".

Drei Faktoren sind für die Entscheidung über die Umfang eines Kennung in JavaScript:

  1. Wie ein Identifikator deklariert wurde
  2. Wenn ein Identifikator deklariert wurde
  3. Ob Sie nun in strenger Modus o nicht-strenger Modus

Einige der Möglichkeiten, wie Bezeichner deklariert werden können:

  1. var , let y const
  2. Parameter der Funktion
  3. Parameter des Fangblocks
  4. Funktionsdeklarationen
  5. Ausdrücke für benannte Funktionen
  6. Implizit definierte Eigenschaften des globalen Objekts (d. h. fehlende var im nicht-strikten Modus)
  7. import Aussagen
  8. eval

Einige der Ortsbezeichnungen können angegeben werden:

  1. Globaler Kontext
  2. Funktion Körper
  3. Gewöhnlicher Block
  4. Der Anfang einer Kontrollstruktur (z.B. Schleife, if, while, etc.)
  5. Körper der Kontrollstruktur
  6. Module

Deklarationsstile

var

Bezeichner, die mit var einen Funktionsumfang haben In diesem Fall werden sie als Eigenschaften des globalen Objekts hinzugefügt und haben einen globalen Geltungsbereich. Es gibt separate Regeln für ihre Verwendung in eval Funktionen.

let und const

Bezeichner, die mit let y const einen Blockumfang haben Sie haben einen globalen Geltungsbereich, außer wenn sie direkt im globalen Kontext deklariert werden.

let , const y var sind alle hochgezogen . Das bedeutet, dass ihre logische Definitionsposition am Anfang des sie umschließenden Bereichs (Block oder Funktion) liegt. Allerdings werden Variablen, die mit let y const kann erst gelesen oder zugewiesen werden, wenn die Kontrolle den Punkt der Deklaration im Quellcode passiert hat. Diese Zwischenzeit wird als zeitliche Totzone bezeichnet.

function f() {
    function g() {
        console.log(x)
    }
    let x = 1
    g()
}
f() // 1 because x is hoisted even though declared with `let`!

Namen der Funktionsparameter

Funktionsparameternamen sind auf den Funktionskörper beschränkt. Beachten Sie, dass dies eine gewisse Komplexität mit sich bringt. Funktionen, die als Standardargumente deklariert sind, schließen über die Parameterliste und nicht den Körper der Funktion.

Funktionsdeklarationen

Funktionsdeklarationen haben im strikten Modus einen Blockbereich und im nicht-strikten Modus einen Funktionsbereich. Hinweis: Der nicht-strikte Modus ist ein komplizierter Satz sich entwickelnder Regeln, die auf den eigenartigen historischen Implementierungen der verschiedenen Browser basieren.

Ausdrücke für benannte Funktionen

Benannte Funktionsausdrücke sind auf sich selbst beschränkt (z. B. zum Zweck der Rekursion).

Implizit definierte Eigenschaften für das globale Objekt

Im nicht-strikten Modus haben implizit definierte Eigenschaften des globalen Objekts globalen Geltungsbereich, da das globale Objekt an der Spitze der Geltungskette steht. Im strikten Modus sind diese nicht zulässig.

eval

Sur eval Zeichenketten, Variablen, die mit var wird in den aktuellen Geltungsbereich gesetzt, oder, falls eval wird indirekt als Eigenschaften des globalen Objekts verwendet.

Beispiele

Das Folgende führt zu einem ReferenceError, weil die Namen x , y y z haben keine Bedeutung außerhalb der Funktion f .

function f() {
    var x = 1
    let y = 1
    const z = 1
}
console.log(typeof x) // undefined (because var has function scope!)
console.log(typeof y) // undefined (because the body of the function is a block)
console.log(typeof z) // undefined (because the body of the function is a block)

Das Folgende löst einen ReferenceError für y y z , aber nicht für x weil die Sichtbarkeit von x ist nicht durch den Block eingeschränkt. Blöcke, die die Körper von Kontrollstrukturen definieren, wie if , for y while verhalten sich ähnlich.

{
    var x = 1
    let y = 1
    const z = 1
}
console.log(x) // 1
console.log(typeof y) // undefined because `y` has block scope
console.log(typeof z) // undefined because `z` has block scope

Im Folgenden, x ist außerhalb der Schleife sichtbar, weil var hat Funktionsumfang:

for(var x = 0; x < 5; ++x) {}
console.log(x) // 5 (note this is outside the loop!)

...wegen dieses Verhaltens müssen Sie vorsichtig sein, wenn Sie Variablen schließen, die mit var in Schleifen. Es gibt nur eine Instanz der Variablen x deklariert und befindet sich logischerweise außerhalb der Schleife.

Die folgenden Ausdrucke 5 fünfmal, und druckt dann 5 ein sechstes Mal für die console.log außerhalb der Schleife:

for(var x = 0; x < 5; ++x) {
    setTimeout(() => console.log(x)) // closes over the `x` which is logically positioned at the top of the enclosing scope, above the loop
}
console.log(x) // note: visible outside the loop

Die folgenden Ausdrucke undefined denn x ist block-scoped. Die Rückrufe werden einer nach dem anderen asynchron ausgeführt. Neues Verhalten für let Variablen bedeutet, dass jede anonyme Funktion über eine andere Variable namens x (anders als es bei var ), und damit ganze Zahlen 0 über 4 gedruckt werden:

for(let x = 0; x < 5; ++x) {
    setTimeout(() => console.log(x)) // `let` declarations are re-declared on a per-iteration basis, so the closures capture different variables
}
console.log(typeof x) // undefined

Das Folgende führt NICHT zu einem ReferenceError weil die Sichtbarkeit von x wird nicht durch den Block eingeschränkt; es wird jedoch gedruckt undefined weil die Variable nicht initialisiert wurde (wegen der if Anweisung).

if(false) {
    var x = 1
}
console.log(x) // here, `x` has been declared, but not initialised

Eine Variable, die am Anfang einer for Schleife mit let ist auf den Körper der Schleife beschränkt:

for(let x = 0; x < 10; ++x) {} 
console.log(typeof x) // undefined, because `x` is block-scoped

Das Folgende löst eine ReferenceError weil die Sichtbarkeit von x wird durch den Block eingeschränkt:

if(false) {
    let x = 1
}
console.log(typeof x) // undefined, because `x` is block-scoped

Variablen, die mit var , let o const sind alle auf Module beschränkt:

// module1.js

var x = 0
export function f() {}

//module2.js

import f from 'module1.js'

console.log(x) // throws ReferenceError

Im Folgenden wird eine Eigenschaft für das globale Objekt deklariert, da Variablen, die mit var innerhalb des globalen Kontexts werden dem globalen Objekt als Eigenschaften hinzugefügt:

var x = 1
console.log(window.hasOwnProperty('x')) // true

let y const im globalen Kontext fügen dem globalen Objekt keine Eigenschaften hinzu, haben aber dennoch globalen Geltungsbereich:

let x = 1
console.log(window.hasOwnProperty('x')) // false

Funktionsparameter können als im Funktionskörper deklariert angesehen werden:

function f(x) {}
console.log(typeof x) // undefined, because `x` is scoped to the function

Die Parameter des Catch-Blocks sind auf den Körper des Catch-Blocks beschränkt:

try {} catch(e) {}
console.log(typeof e) // undefined, because `e` is scoped to the catch block

Benannte Funktionsausdrücke sind nur auf den Ausdruck selbst beschränkt:

(function foo() { console.log(foo) })()
console.log(typeof foo) // undefined, because `foo` is scoped to its own expression

Im nicht-strikten Modus sind implizit definierte Eigenschaften des globalen Objekts global skaliert. Im strikten Modus erhalten Sie einen Fehler.

x = 1 // implicitly defined property on the global object (no "var"!)

console.log(x) // 1
console.log(window.hasOwnProperty('x')) // true

Im nicht-strikten Modus haben Funktionsdeklarationen einen Funktionsumfang. Im strikten Modus haben sie den Geltungsbereich eines Blocks.

'use strict'
{
    function foo() {}
}
console.log(typeof foo) // undefined, because `foo` is block-scoped

Wie es unter der Haube funktioniert

Der Geltungsbereich ist definiert als die lexikalisch Bereich des Codes, für den ein Bezeichner gültig ist.

In JavaScript hat jedes Funktionsobjekt eine versteckte [[Environment]] Referenz, die eine Referenz auf den lexikalische Umgebung de la Ausführungskontext (Stapelrahmen), in dem er erstellt wurde.

Wenn Sie eine Funktion aufrufen, wird die versteckte [[Call]] Methode aufgerufen wird. Diese Methode erzeugt einen neuen Ausführungskontext und stellt eine Verbindung zwischen dem neuen Ausführungskontext und der lexikalischen Umgebung des Funktionsobjekts her. Dies geschieht durch Kopieren der [[Environment]] Wert des Funktions-Objekts in eine äußerer Bezug in der lexikalischen Umgebung des neuen Ausführungskontexts.

Beachten Sie, dass diese Verbindung zwischen dem neuen Ausführungskontext und der lexikalischen Umgebung des Funktionsobjekts als Verschluss .

In JavaScript wird der Geltungsbereich also durch lexikalische Umgebungen implementiert, die durch äußere Referenzen in einer "Kette" miteinander verbunden sind. Diese Kette von lexikalischen Umgebungen wird als Scope-Kette bezeichnet, und die Auflösung von Bezeichnern erfolgt durch Suche in der Kette nach einem passenden Bezeichner.

Herausfinden mehr .

295 Stimmen

Nicht einmal annähernd umfassend, aber das ist vielleicht das Muss wissen, eine Reihe von Javascript Umfang Tricks braucht man, um effektiv sogar READ modernen Javascript.

0 Stimmen

In Anbetracht Ihrer Bemerkung zum "Ranking" werde ich wohl anfangen, mehr über diese Art von Dingen zu lesen. Aber es ist nicht so schlimm, wie ich dachte, ich habe die Logik dahinter ziemlich gut verstanden. Obwohl, über die Objekteigenschaften, wenn ich es global deklarieren wird es von object.prototype referenziert werden?

0 Stimmen

Danke, was ich hinzufügen möchte ist, wenn Sie Datei undefiniert ist, versuchen Sie mit neuen Array oder neue String und so weiter

243voto

krosenvold Punkte 73093

Javascript verwendet Anwendungsbereichsketten, um den Anwendungsbereich für eine bestimmte Funktion festzulegen. Normalerweise gibt es einen globalen Bereich, und jede definierte Funktion hat ihren eigenen verschachtelten Bereich. Jede Funktion, die innerhalb einer anderen Funktion definiert ist, hat einen lokalen Geltungsbereich, der mit der äußeren Funktion verbunden ist. Es ist immer die Position im Quelltext, die den Bereich definiert.

Ein Element in der Bereichskette ist im Grunde eine Map mit einem Zeiger auf seinen übergeordneten Bereich.

Wenn eine Variable aufgelöst wird, beginnt Javascript mit dem innersten Bereich und sucht nach außen.

2 Stimmen

Umfangsketten sind ein anderer Begriff für [Speicher] Schließungen ... für diejenigen, die hier lesen, um javascript zu lernen/einzusteigen.

115voto

Jon Skeet Punkte 1325502

Global deklarierte Variablen haben einen globalen Geltungsbereich. Variablen, die innerhalb einer Funktion deklariert werden, sind auf diese Funktion beschränkt und überschatten gleichnamige globale Variablen.

(Ich bin sicher, dass es viele Feinheiten gibt, auf die echte JavaScript-Programmierer in anderen Antworten hinweisen können. Insbesondere bin ich auf Folgendes gestoßen diese Seite über was genau this zu jeder Zeit bedeutet. Hoffentlich dieser mehr einführende Link reicht jedoch für den Anfang.)

8 Stimmen

Ich habe Angst, diese Frage auch nur ansatzweise zu beantworten. Als echter Javascript-Programmierer weiß ich, wie schnell die Antwort aus dem Ruder laufen könnte. Nette Artikel.

11 Stimmen

@Triptych: Ich weiß, was Sie meinen, wenn die Dinge aus dem Ruder laufen, aber bitte trotzdem eine Antwort hinzufügen. Ich habe die obige Antwort nur durch ein paar Suchanfragen erhalten... eine Antwort, die von jemandem mit tatsächlicher Erfahrung geschrieben wurde, ist gebunden um besser zu sein. Bitte korrigieren Sie jede meiner Antworten, die definitiv falsch ist!

6 Stimmen

Irgendwie ist Jon Skeet für MEINE beliebteste Antwort auf Stack Overflow verantwortlich.

80voto

John Slegers Punkte 41127

JavaScript der alten Schule

Traditionell gibt es in JavaScript eigentlich nur zwei Arten von Geltungsbereichen:

  1. Globaler Geltungsbereich : Die Variablen sind während der gesamten Anwendung bekannt, vom Beginn der Anwendung an (*)
  2. Funktionsumfang : Variablen sind bekannt innerhalb die Funktion in der sie deklariert sind, vom Beginn der Funktion an (*)

Ich werde nicht näher darauf eingehen, da es bereits viele andere Antworten gibt, die den Unterschied erklären.


Modernes JavaScript

Le site die neuesten JavaScript-Spezifikationen erlauben nun auch einen dritten Anwendungsbereich:

  1. Blockumfang : Identifikatoren sind "bekannt" vom Anfang des Bereichs aus, in dem sie deklariert werden Sie können jedoch erst nach der Zeile, in der sie deklariert wurden, zugewiesen oder dereferenziert (gelesen) werden. Diese Zwischenzeit wird als "temporale Totzone" bezeichnet.

Wie erstelle ich Blockbereichsvariablen?

Traditionell erstellen Sie Ihre Variablen wie folgt:

var myVariable = "Some text";

Blockbereichsvariablen werden wie folgt erstellt:

let myVariable = "Some text";

Was ist also der Unterschied zwischen Funktionsumfang und Blockumfang?

Um den Unterschied zwischen Funktionsumfang und Blockumfang zu verstehen, betrachten Sie den folgenden Code:

// i IS NOT known here
// j IS NOT known here
// k IS known here, but undefined
// l IS NOT known here

function loop(arr) {
    // i IS known here, but undefined
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( var i = 0; i < arr.length; i++ ) {
        // i IS known here, and has a value
        // j IS NOT known here
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( let j = 0; j < arr.length; j++ ) {
        // i IS known here, and has a value
        // j IS known here, and has a value
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here
}

loop([1,2,3,4]);

for( var k = 0; k < arr.length; k++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS NOT known here
};

for( let l = 0; l < arr.length; l++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS known here, and has a value
};

loop([1,2,3,4]);

// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here

Hier können wir sehen, dass unsere Variable j ist nur in der ersten for-Schleife bekannt, nicht aber davor und danach. Dennoch ist unsere Variable i ist in der gesamten Funktion bekannt.

Bedenken Sie auch, dass blockscoped-Variablen nicht bekannt sind, bevor sie deklariert werden, da sie nicht gehostet werden. Es ist auch nicht erlaubt, dieselbe blockscoped Variable innerhalb desselben Blocks erneut zu deklarieren. Dadurch sind blockscoped-Variablen weniger fehleranfällig als global oder funktional scoped-Variablen, die hoisted sind und bei Mehrfachdeklarationen keine Fehler verursachen.


Ist die Verwendung von Blockbereichsvariablen heute sicher?

Ob es heute sicher ist oder nicht, hängt von Ihrer Umgebung ab:

  • Wenn Sie serverseitigen JavaScript-Code schreiben ( Node.js ), können Sie sicher die let Erklärung.

  • Wenn Sie clientseitigen JavaScript-Code schreiben und einen browserbasierten Transpiler verwenden (wie Traceur o babel-standalone ), können Sie sicher die let Anweisung ist Ihr Code jedoch wahrscheinlich alles andere als optimal in Bezug auf die Leistung.

  • Wenn Sie clientseitigen JavaScript-Code schreiben und einen Node-basierten Transpiler (wie den Traceur-Shell-Skript o Babel ), können Sie sicher die let Erklärung. Und da Ihr Browser nur den umgesetzten Code kennt, dürften sich die Leistungseinbußen in Grenzen halten.

  • Wenn Sie clientseitigen JavaScript-Code schreiben und keinen Transpiler verwenden, müssen Sie die Browserunterstützung berücksichtigen.

    Die folgenden Browser unterstützen die folgenden Funktionen nicht let überhaupt nicht:

    • Internetexplorer 10 und darunter
    • Firefox 43 und darunter
    • Safari 9 und darunter
    • Android-Browser 4 und darunter
    • Oper 27 und darunter
    • Chome 40 und darunter
    • Jegliche Version von Opera Mini & Blackberry-Browser

enter image description here


So behalten Sie den Überblick über die Browserunterstützung

Für einen aktuellen Überblick darüber, welche Browser die let Aussage zum Zeitpunkt des Lesens dieser Antwort, siehe este Can I Use Seite .


(*) Globale und funktional skalierte Variablen können initialisiert und verwendet werden, bevor sie deklariert werden, da JavaScript-Variablen hochgezogen. . Das bedeutet, dass Deklarationen immer ganz oben im Geltungsbereich stehen.

2 Stimmen

"IST NICHT BEKANNT" ist irreführend, da die Variable dort aufgrund des Hochziehens deklariert wird.

0 Stimmen

Das obige Beispiel ist irreführend, die Variablen 'i' und 'j' sind außerhalb des Blocks nicht bekannt. Die 'Let'-Variablen haben nur in diesem bestimmten Block einen Geltungsbereich, nicht außerhalb des Blocks. Let hat auch andere Vorteile, man kann die Variable nicht erneut deklarieren und sie hat den lexikalischen Geltungsbereich.

0 Stimmen

@Oriol : Endlich habe ich es geschafft, meine Antwort zu verbessern und das Thema Heben anzusprechen. Danke für den Hinweis, dass meine Antwort verbesserungswürdig ist. Ich habe auch ein paar andere Verbesserungen vorgenommen.

43voto

geowa4 Punkte 38662

Hier ist ein Beispiel:

<script>

var globalVariable = 7; //==window.globalVariable

function aGlobal( param ) { //==window.aGlobal(); 
                            //param is only accessible in this function
  var scopedToFunction = {
    //can't be accessed outside of this function

    nested : 3 //accessible by: scopedToFunction.nested
  };

  anotherGlobal = {
    //global because there's no `var`
  }; 

}

</script>

Sie werden sich mit Verschlüssen befassen wollen und damit, wie man sie verwendet, um private Mitglieder .

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