Was ist der Umfang von Variablen in JavaScript?


2013

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


4
Hier ist ein weiterer netter Link , um dieses Problem zu berücksichtigen: " Erläutern des JavaScript-Bereichs und der Schließungen ".
Full-Stack Software Engineer

9
Hier ist ein Artikel, der es sehr schön erklärt. Alles, was Sie über den Umfang der Javascript-Variablen wissen müssen
Saurab Parakh

2
Das zuvor erwähnte E-Book von Kyle Simpson kann auf Github gelesen werden und enthält alles, was Sie über JavaScript Scopes & Closures wissen müssen. Sie finden es hier: github.com/getify/You-Dont-Know-JS/blob/master/… Es ist Teil der Buchreihe "Sie kennen JS nicht" , die sich hervorragend für alle eignet, die es wissen möchten mehr über JavaScript.
3rik82

Antworten:


2536

TLDR

JavaScript hat lexikalische (auch statische) Gültigkeitsbereiche und Schließungen. Dies bedeutet, dass Sie den Umfang eines Bezeichners anhand des Quellcodes erkennen können.

Die vier Bereiche sind:

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

Außerhalb der Sonderfälle des globalen Bereichs und des Modulbereichs werden Variablen mit var(Funktionsbereich), let(Blockbereich) und const(Blockbereich) deklariert . Die meisten anderen Formen der Bezeichnerdeklaration haben einen Blockbereich im strengen Modus.

Überblick

Der Bereich ist der Bereich der Codebasis, über den ein Bezeichner gültig ist.

Eine lexikalische Umgebung ist eine Zuordnung zwischen Bezeichnernamen und den damit verbundenen Werten.

Der Bereich besteht aus einer verknüpften Verschachtelung lexikalischer Umgebungen, wobei jede Ebene in der Verschachtelung einer lexikalischen Umgebung eines Ahnenausführungskontexts entspricht.

Diese verknüpften lexikalischen Umgebungen bilden eine Bereichskette. Bei der Bezeichnerauflösung wird entlang dieser Kette nach einem passenden Bezeichner gesucht.

Die Auflösung der Kennung erfolgt nur in eine Richtung: nach außen. Auf diese Weise können äußere lexikalische Umgebungen nicht in innere lexikalische Umgebungen "sehen".

Es gibt drei relevante Faktoren bei der Entscheidung über den Umfang eines Bezeichners in JavaScript:

  1. Wie ein Bezeichner deklariert wurde
  2. Wo eine Kennung deklariert wurde
  3. Ob Sie sich im strengen oder im nicht strengen Modus befinden

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

  1. var, letundconst
  2. Funktionsparameter
  3. Catch-Block-Parameter
  4. Funktionsdeklarationen
  5. Benannte Funktionsausdrücke
  6. Implizit definierte Eigenschaften für das globale Objekt (dh varim nicht strengen Modus fehlen )
  7. import Aussagen
  8. eval

Einige der Standortkennungen können deklariert werden:

  1. Globaler Kontext
  2. Funktionskörper
  3. Gewöhnlicher Block
  4. Die Oberseite einer Kontrollstruktur (z. B. Schleife, wenn, während usw.)
  5. Kontrollstrukturkörper
  6. Module

Deklarationsstile

var

Mit deklarierte Bezeichner var haben einen Funktionsumfang , außer wenn sie direkt im globalen Kontext deklariert werden. In diesem Fall werden sie als Eigenschaften für das globale Objekt hinzugefügt und haben einen globalen Gültigkeitsbereich. Es gibt separate Regeln für ihre Verwendung in evalFunktionen.

let und const

Bezeichner, die mit deklariert wurden letund const einen Blockbereich haben , außer wenn sie direkt im globalen Kontext deklariert werden. In diesem Fall haben sie einen globalen Bereich.

Hinweis: let, constund var sind alle hochgezogen . Dies bedeutet, dass ihre logische Definitionsposition die Spitze ihres umschließenden Bereichs (Block oder Funktion) ist. Variablen, die als verwendet deklariert wurden letund consterst gelesen oder zugewiesen werden können, wenn die Steuerung den Deklarationspunkt im Quellcode überschritten hat. Die 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`!

Funktionsparameternamen

Funktionsparameternamen sind auf den Funktionskörper beschränkt. Beachten Sie, dass dies eine leichte Komplexität aufweist. Als Standardargumente deklarierte Funktionen schließen über der Parameterliste und nicht über dem Hauptteil der Funktion.

Funktionsdeklarationen

Funktionsdeklarationen haben einen Blockbereich im strengen Modus und einen Funktionsbereich im nicht strengen Modus. Hinweis: Der nicht strenge Modus ist ein komplizierter Satz neuer Regeln, die auf den skurrilen historischen Implementierungen verschiedener Browser basieren.

Benannte Funktionsausdrücke

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 strengen Modus haben implizit definierte Eigenschaften für das globale Objekt einen globalen Bereich, da sich das globale Objekt am oberen Rand der Bereichskette befindet. Im strengen Modus sind diese nicht zulässig.

eval

In evalZeichenfolgen werden mit deklarierte Variablen varim aktuellen Bereich platziert oder, wenn evalsie indirekt verwendet werden, als Eigenschaften für das globale Objekt.

Beispiele

Im Folgenden wird ein Reference weil die Namen werfen x, yund zhaben 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)

Im Folgenden wird ein ReferenceError für yund ausgelöst z, jedoch nicht für x, da die Sichtbarkeit von xnicht durch den Block eingeschränkt wird. Blöcke, die die Körper von Kontrollstrukturen definieren möchten if, forund while, ähnlich verhalten.

{
    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 xist außerhalb der Schleife sichtbar, da varFunktionsumfang hat:

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 varin Schleifen deklariert wurden . Hier ist nur eine Instanz einer Variablen xdeklariert, die sich logisch außerhalb der Schleife befindet.

Die folgenden Drucke 5werden fünfmal und dann 5zum sechsten Mal console.logaußerhalb der Schleife gedruckt :

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

Das Folgende wird gedruckt, undefinedweil xes einen Blockbereich hat. Die Rückrufe werden einzeln asynchron ausgeführt. Neues Verhalten für letVariablen bedeutet, dass jede anonyme Funktion über einer anderen Variablen mit dem Namen geschlossen wird x(anders als dies der Fall gewesen wäre var) und daher Ganzzahlen 0durchgedruckt 4werden:

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

Im Folgenden wird KEIN a ausgelöst, ReferenceErrorda die Sichtbarkeit von xnicht durch den Block eingeschränkt wird. Es wird jedoch gedruckt, undefinedda die Variable nicht initialisiert wurde (aufgrund der ifAnweisung).

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

Eine Variable, die am Anfang einer forSchleife mit letdeklariert wird, ist auf den Hauptteil der Schleife beschränkt:

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

Im Folgenden wird a ausgelöst, ReferenceErrorda die Sichtbarkeit von xdurch den Block eingeschränkt wird:

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

Variablen, die mit oder für Module deklariert wurden var, sind alle auf Module beschränkt:letconst

// 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 varim globalen Kontext deklarierte Variablen als Eigenschaften zum globalen Objekt hinzugefügt werden:

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

letund constfügen Sie im globalen Kontext keine Eigenschaften zum globalen Objekt hinzu, haben aber dennoch einen globalen Bereich:

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

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

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

Catch-Block-Parameter sind auf den Catch-Block-Body beschränkt:

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

Benannte Funktionsausdrücke beziehen sich nur auf den Ausdruck selbst:

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

Im nicht strengen Modus werden implizit definierte Eigenschaften für das globale Objekt global definiert. Im strengen Modus wird eine Fehlermeldung angezeigt.

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

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

Im nicht strengen Modus haben Funktionsdeklarationen einen Funktionsumfang. Im strengen Modus haben sie Blockbereich.

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

Wie es unter der Haube funktioniert

Der Bereich ist definiert als der lexikalische Codebereich, über den ein Bezeichner gültig ist.

In JavaScript hat jedes Funktionsobjekt eine versteckte [[Environment]]Referenz, die auf die lexikalische Umgebung des Ausführungskontexts (Stapelrahmen) verweist, in dem es erstellt wurde.

Wenn Sie eine Funktion aufrufen, wird die versteckte [[Call]]Methode aufgerufen. Diese Methode erstellt einen neuen Ausführungskontext und stellt eine Verbindung zwischen dem neuen Ausführungskontext und der lexikalischen Umgebung des Funktionsobjekts her. Dazu wird der [[Environment]]Wert für das Funktionsobjekt in ein äußeres Referenzfeld in der lexikalischen Umgebung des neuen Ausführungskontexts kopiert .

Beachten Sie, dass diese Verknüpfung zwischen dem neuen Ausführungskontext und der lexikalischen Umgebung des Funktionsobjekts als Abschluss bezeichnet wird .

Daher wird in JavaScript der Gültigkeitsbereich über lexikalische Umgebungen implementiert, die durch äußere Referenzen in einer "Kette" miteinander verbunden sind. Diese Kette von lexikalischen Umgebungen wird als Bereichskette bezeichnet, und die Auflösung von Bezeichnern erfolgt durch Durchsuchen der Kette nach einem passenden Bezeichner.

Erfahren Sie mehr .


280
Nicht einmal annähernd umfassend, aber dies ist vielleicht die unverzichtbare Reihe von Javascript-Tricks, die man braucht, um sogar modernes Javascript effektiv zu lesen.
Triptychon

148
Eine hoch bewertete Antwort, nicht sicher warum. Es handelt sich nur um eine Reihe von Beispielen ohne angemessene Erklärung, die dann die Vererbung von Prototypen (dh die Auflösung von Eigenschaften) mit der Bereichskette (dh die variable Auflösung) zu verwechseln scheinen. Eine umfassende (und genau) Erklärung von Umfang und Eigentum Auflösung ist in den comp.lang.javascript FAQ Notizen .
RobG

109
@RobG Es wird hoch bewertet, weil es für eine breite Palette von Programmierern nützlich und verständlich ist, ungeachtet kleinerer Katachresen. Der Link, den Sie gepostet haben, ist zwar für einige Fachleute nützlich, aber für die meisten Leute, die heute Javascript schreiben, unverständlich. Fühlen Sie sich frei, Nomenklaturprobleme durch Bearbeiten der Antwort zu beheben.
Triptychon

7
@ triptych - Ich bearbeite nur Antworten, um kleinere Dinge zu beheben, nicht größere. Durch Ändern von "Bereich" in "Eigenschaft" wird der Fehler behoben, nicht jedoch das Problem der Vermischung von Vererbung und Bereich ohne eine sehr klare Unterscheidung.
RobG

24
Wenn Sie eine Variable im äußeren Bereich definieren und dann eine if-Anweisung haben, definieren Sie eine Variable innerhalb der Funktion mit demselben Namen, auch wenn diese, wenn der Zweig nicht erreicht wird, neu definiert wird. Ein Beispiel - jsfiddle.net/3CxVm
Chris S

233

Javascript verwendet Bereichsketten, um den Bereich für eine bestimmte Funktion festzulegen. In der Regel gibt es einen globalen Bereich, und jede definierte Funktion verfügt über einen eigenen verschachtelten Bereich. Jede in einer anderen Funktion definierte Funktion hat einen lokalen Bereich, der mit der äußeren Funktion verknüpft ist. Es ist immer die Position in der Quelle, die den Bereich definiert.

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

Beim Auflösen einer Variablen beginnt Javascript im innersten Bereich und sucht nach außen.


1
Scope Chains sind ein weiterer Begriff für [Memory] Closures ... für diejenigen, die hier lesen, um Javascript zu lernen / zu erlernen.
New Alexandria

108

Global deklarierte Variablen haben einen globalen Gültigkeitsbereich. Innerhalb einer Funktion deklarierte Variablen haben einen Gültigkeitsbereich für diese Funktion und schattieren globale Variablen mit demselben Namen.

(Ich bin sicher, dass es viele Feinheiten gibt, auf die echte JavaScript-Programmierer in anderen Antworten hinweisen können. Insbesondere bin ich auf diese Seite gestoßen, was genau genau thiszu jeder Zeit bedeutet. Hoffentlich reicht dieser einführende Link aus, um Ihnen den Einstieg zu erleichtern .)


7
Ich habe Angst, diese Frage überhaupt zu beantworten. Als echter Javascript-Programmierer weiß ich, wie schnell die Antwort außer Kontrolle geraten kann. Schöne Artikel.
Triptychon

10
@ Triptychon: Ich weiß, was du damit meinst, dass Dinge außer Kontrolle geraten, aber bitte füge trotzdem eine Antwort hinzu. Ich habe die oben nur ein paar Suchanfrage zu tun ... eine Antwort von jemandem mit tatsächlichen Erfahrungen geschrieben gebunden , besser zu sein. Bitte korrigieren Sie eine meiner Antworten, die definitiv falsch ist!
Jon Skeet

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

75

JavaScript der alten Schule

Traditionell hat JavaScript nur zwei Arten von Gültigkeitsbereichen:

  1. Globaler Geltungsbereich : Variablen sind in der gesamten Anwendung ab dem Start der Anwendung bekannt (*).
  2. Funktionsumfang : Variablen sind innerhalb der Funktion, in der sie deklariert sind, vom Beginn der Funktion an bekannt (*)

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


Modernes JavaScript

Die neuesten JavaScript-Spezifikationen ermöglichen jetzt auch einen dritten Bereich:

  1. Blockbereich : Bezeichner sind vom oberen Rand des Bereichs, in dem sie deklariert sind, "bekannt" , können jedoch erst nach der Zeile ihrer Deklaration zugewiesen oder dereferenziert (gelesen) werden. Diese Zwischenzeit wird als "zeitliche Totzone" bezeichnet.

Wie erstelle ich Blockbereichsvariablen?

Traditionell erstellen Sie Ihre Variablen folgendermaßen:

var myVariable = "Some text";

Blockbereichsvariablen werden wie folgt erstellt:

let myVariable = "Some text";

Was ist also der Unterschied zwischen Funktionsumfang und Blockumfang?

Beachten Sie den folgenden Code, um den Unterschied zwischen Funktionsumfang und Blockumfang zu verstehen:

// 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 jnur in der ersten for-Schleife bekannt ist, nicht jedoch vorher und nachher. Unsere Variable iist jedoch in der gesamten Funktion bekannt.

Beachten Sie auch, dass Variablen mit Blockbereich nicht bekannt sind, bevor sie deklariert werden, da sie nicht angehoben werden. Sie dürfen auch nicht dieselbe Variable mit Blockbereich innerhalb desselben Blocks neu deklarieren. Dies macht Variablen mit Blockbereich weniger fehleranfällig als Variablen mit globalem oder funktionalem Bereich, die angehoben werden und bei mehreren Deklarationen keine Fehler verursachen.


Ist es heute sicher, Blockbereichsvariablen zu verwenden?

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

  • Wenn Sie serverseitigen JavaScript-Code ( Node.js ) schreiben , können Sie die letAnweisung sicher verwenden .

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

  • Wenn Sie clientseitigen JavaScript-Code schreiben und einen knotenbasierten Transpiler verwenden (wie das Traceur-Shell-Skript oder Babel ), können Sie die letAnweisung sicher verwenden . Und da Ihr Browser nur über den transpilierten Code Bescheid weiß, sollten die Leistungsnachteile begrenzt sein.

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

    Dies sind einige Browser, die überhaupt nicht unterstützen let:

    • Internet Explorer 10 und darunter
    • Firefox 43 und darunter
    • Safari 9 und darunter
    • Android Browser 4 und niedriger
    • Opera 27 und darunter
    • Chome 40 und darunter
    • JEDE Version von Opera Mini & Blackberry Browser

Geben Sie hier die Bildbeschreibung ein


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

Auf dieser Seite finden Sie eine aktuelle Übersicht darüber, welche Browser die letAussage zum Zeitpunkt des Lesens dieser Antwort unterstützen .Can I Use


(*) Global und funktional scoped Variablen initialisiert und verwendet werden , bevor sie deklariert werden , da JavaScript - Variablen werden gehisst . Dies bedeutet, dass Deklarationen immer ganz oben im Geltungsbereich stehen.


2
"IST NICHT bekannt" ist irreführend, da die Variable dort aufgrund des Hebens deklariert wird.
Oriol

Das obige Beispiel ist irreführend. Die Variablen 'i' und 'j' sind außerhalb des Blocks nicht bekannt. 'Let'-Variablen haben nur in diesem bestimmten Block Gültigkeitsbereich, nicht außerhalb des Blocks. Let hat auch andere Vorteile: Sie können die Variable nicht erneut deklarieren und sie enthält den lexikalischen Bereich.
Zakir

1
Das war hilfreich, danke! Ich denke, es wäre noch hilfreicher, genau zu sagen, was Sie unter "Modern JavaScript" und "Old School JavaScript" verstehen. Ich denke, diese entsprechen ECMAScript 6 / ES6 / ECMAScript 2015 bzw. früheren Versionen?
Jon Schneider

1
@ JonSchneider: Richtig! Wenn ich "JavaScript der alten Schule" sage, spreche ich nicht über ECMAScript 5, und wenn ich mich auf "modernes JavaScript" beziehe, nehme ich ECMAScript 6 (auch bekannt als ECMAScript 2015). Ich dachte nicht, dass es wirklich so wichtig ist, hier ins Detail zu gehen, da die meisten Leute nur wissen wollen, (1) was der Unterschied zwischen Blockumfang und Funktionsumfang ist, (2) welche Browser den Blockumfang unterstützen und (3) ob es heute sicher ist, Block Scope für jedes Projekt zu verwenden, an dem sie arbeiten. Deshalb habe ich meine Antwort darauf konzentriert, diese Probleme anzugehen.
John Slegers

1
@ JonSchneider: (Fortsetzung) Trotzdem habe ich gerade einen Link zu einem Artikel des Smashing Magazine über ES6 / ES2015 hinzugefügt, für diejenigen, die mehr darüber erfahren möchten, welche Funktionen in den letzten Jahren zu JavaScript hinzugefügt wurden ... von allen anderen, die Vielleicht frage ich mich, was ich mit "modernem JavaScript" meine.
John Slegers

39

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 sollten die Schließungen untersuchen und untersuchen, wie Sie sie verwenden können, um private Mitglieder zu gewinnen .



26

In "Javascript 1.7" (Mozillas Erweiterung zu Javascript) kann man auch Block-Scope-Variablen mit der folgenden letAnweisung deklarieren :

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

2
Ja, aber ist es sicher zu benutzen? Ich meine, würde ich diese Implementierung realistisch wählen, wenn mein Code in WebKit ausgeführt wird?
IgorGanapolsky

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

Ich denke, die einzig gültige Verwendung hierfür wäre, wenn Sie wüssten, dass alle Clients einen Mozilla-Browser wie für ein unternehmensinternes System verwenden würden.
GazB

Oder wenn Sie mit dem XUL-Framework programmieren, dem Mozilla-Interface-Framework, in dem Sie mit CSS, XML und Javascript erstellen.
Gerard ONeill

1
@ GazB auch das ist eine schreckliche Idee! Heute wissen Sie also, dass Ihre Kunden Mozilla verwenden. In einem neuen Memo heißt es, dass sie jetzt etwas anderes verwenden. IE der Grund, warum unser Bezahlsystem scheiße ist ... Sie müssen IE8 verwenden und niemals IE9 oder IE10 oder Firefox oder Chrome, weil es
einfach

25

Die Idee, in JavaScript zu scoping, als es ursprünglich von Brendan Eich entworfen wurde, kam von der HyperCard- Skriptsprache HyperTalk .

In dieser Sprache wurden die Anzeigen ähnlich wie bei einem Stapel Karteikarten erstellt. Es gab eine Hauptkarte, die als Hintergrund bezeichnet wurde. Es war transparent und kann als unterste Karte angesehen werden. Alle Inhalte auf dieser Basiskarte wurden mit darauf platzierten Karten geteilt. Jede oben platzierte Karte hatte ihren eigenen Inhalt, der Vorrang vor der vorherigen Karte hatte, aber auf Wunsch immer noch Zugriff auf die vorherigen Karten hatte.

Genau so ist das JavaScript-Scoping-System aufgebaut. Es hat nur verschiedene Namen. Die Karten in JavaScript werden als Ausführungskontexte ECMA bezeichnet . Jeder dieser Kontexte enthält drei Hauptteile. Eine variable Umgebung, eine lexikalische Umgebung und diese Bindung. Zurück zur Kartenreferenz: Die lexikalische Umgebung enthält den gesamten Inhalt früherer Karten, die sich weiter unten im Stapel befinden. Der aktuelle Kontext befindet sich oben im Stapel, und alle dort deklarierten Inhalte werden in der variablen Umgebung gespeichert. Die variable Umgebung hat bei Namenskollisionen Vorrang.

Diese Bindung zeigt auf das enthaltende Objekt. Manchmal ändern sich Bereiche oder Ausführungskontexte, ohne dass sich das enthaltende Objekt ändert, z. B. in einer deklarierten Funktion, in der sich das enthaltende Objekt befinden kann, windowoder in einer Konstruktorfunktion.

Diese Ausführungskontexte werden jedes Mal erstellt, wenn die Steuerung übertragen wird. Die Steuerung wird übertragen, wenn die Ausführung des Codes beginnt, und dies erfolgt hauptsächlich über die Funktionsausführung.

Das ist also die technische Erklärung. In der Praxis ist es wichtig, sich dies in JavaScript zu merken

  • Bereiche sind technisch "Ausführungskontexte"
  • Kontexte bilden einen Stapel von Umgebungen, in denen Variablen gespeichert sind
  • Die Oberseite des Stapels hat Vorrang (die Unterseite ist der globale Kontext).
  • Jede Funktion erstellt einen Ausführungskontext (aber nicht immer eine neue dieser Bindung)

Wenn Sie dies auf eines der vorherigen Beispiele (5. "Schließen") auf dieser Seite anwenden, können Sie dem Stapel von Ausführungskontexten folgen. In diesem Beispiel befinden sich drei Kontexte im Stapel. Sie werden durch den äußeren Kontext, den Kontext in der sofort aufgerufenen Funktion, die von var six aufgerufen wird, und den Kontext in der zurückgegebenen Funktion innerhalb der sofort aufgerufenen Funktion von var six definiert.

i ) Der äußere Kontext. Es 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 im Stapel Vorrang hat
iii ) Der zurückgegebene Funktionskontext hat eine lexikalische Umgebung Umgebung von a = 6 und das ist der Wert, auf den in der Warnung verwiesen wird, wenn sie aufgerufen wird.

Geben Sie hier die Bildbeschreibung ein


17

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

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

3) Die Eigenschaften werden durch Durchlaufen der Prototypenkette aufgelöst. Die with-Anweisung bringt Objekteigenschaftsnamen in den durch den with-Block definierten lexikalischen Bereich.

BEARBEITEN: ECMAAScript 6 (Harmony) unterstützt let, und ich weiß, dass Chrome ein 'Harmony'-Flag zulässt, also unterstützt es es vielleicht.

Es wäre eine Unterstützung für das Scoping auf Blockebene, aber Sie müssen das Schlüsselwort verwenden, um dies zu erreichen.

BEARBEITEN: Basierend auf Benjamins Hinweis auf die with- und catch-Anweisungen in den Kommentaren habe ich den Beitrag bearbeitet und weitere hinzugefügt. Sowohl die with- als auch die catch-Anweisung führen Variablen in ihre jeweiligen Blöcke ein, und das ist ein Blockbereich. Diese Variablen sind auf die Eigenschaften der an sie übergebenen Objekte ausgerichtet.

 //chrome (v8)

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

EDIT: Klärendes Beispiel:

test1 hat einen Gültigkeitsbereich für den with-Block, ist jedoch auf a.test1 ausgerichtet. 'Var test1' erstellt eine neue Variable test1 im oberen lexikalischen Kontext (Funktion oder global), es sei denn, es ist eine Eigenschaft von a - was es ist.

Huch! Seien Sie vorsichtig mit 'with' - genau wie var ein Noop ist, wenn die Variable bereits in der Funktion definiert ist, ist es auch ein Noop in Bezug auf aus dem Objekt importierte Namen! Ein kleiner Hinweis auf den bereits definierten Namen würde dies viel sicherer machen. Ich persönlich werde aus diesem Grund nie mit verwenden.


Sie haben hier einige Fehler, denn ein JavaScript hat Formen des Block Scoping.
Benjamin Gruenbaum

Meine Ohren (Augen) sind offen, Benjamin - Meine obigen Aussagen beziehen sich darauf, wie ich mit Javascript-Scoping umgegangen bin, aber sie basieren nicht auf dem Lesen der Spezifikation. Und ich hoffe, Sie beziehen sich nicht auf die with-Anweisung (eine Form des Objekt-Scoping) oder Mozillas spezielle 'let'-Syntax.
Gerard ONeill

Nun, withAussage ist aber eine Form des Block Scopingcatch Klauseln sind eine viel häufigere Form (Fun fact, v8 implementiert catchmit a with) - das sind so ziemlich die einzigen Formen des Block-Scoping in JavaScript selbst ( dh Funktion, global, try / catch (mit und ihre Ableitungen) haben Host-Umgebungen jedoch unterschiedliche Vorstellungen von Scoping - zum Beispiel Inline-Ereignisse im Browser und im VM-Modul von NodeJS.
Benjamin Gruenbaum

Benjamin - soweit ich sehen kann, führen sowohl mit als auch catch das Objekt nur in den aktuellen Bereich (und damit in die Eigenschaften) ein, aber nach dem Ende des jeweiligen Blocks werden die Variablen zurückgesetzt. Zum Beispiel hat eine neue Variable, die in einem catch eingeführt wird, den Umfang der einschließenden Funktion / Methode.
Gerard ONeill

2
Welches ist genau das, was Block Scoping bedeutet :)
Benjamin Gruenbaum

9

Ich habe festgestellt, dass viele JavaScript-Neulinge Probleme haben zu verstehen, dass die Vererbung standardmäßig in der Sprache verfügbar ist und dass der Funktionsumfang bisher der einzige Bereich ist. Ich habe eine Erweiterung für einen Verschönerer bereitgestellt, den ich Ende letzten Jahres geschrieben habe und der JSPretty heißt. Der Funktionsumfang der Feature-Farben im Code ordnet allen in diesem Bereich deklarierten Variablen immer eine Farbe zu. Das Schließen wird visuell demonstriert, wenn eine Variable mit einer Farbe aus einem Bereich in einem anderen Bereich verwendet wird.

Probieren Sie die Funktion unter:

Eine Demo finden Sie unter:

Den Code finden Sie unter:

Derzeit bietet die Funktion Unterstützung für eine Tiefe von 16 verschachtelten Funktionen, färbt jedoch derzeit keine globalen Variablen.


1
Funktioniert bei Firefox 26 nicht. Ich füge Code ein oder lade eine Datei, klicke auf Ausführen und nichts passiert.
Mplwork

Umfang und Vererbung sind zwei unterschiedliche Dinge.
Ben Aston

9

JavaScript hat nur zwei Arten von Gültigkeitsbereichen:

  1. Globaler Bereich : Global ist nichts anderes als ein Bereich auf Fensterebene. Hier ist eine Variable in der gesamten Anwendung vorhanden.
  2. Funktionsumfang : Die in einer Funktion mit varSchlüsselwort deklarierte Variable hat einen Funktionsumfang.

Bei jedem Aufruf einer Funktion wird ein Variablenbereichsobjekt erstellt (und in die Bereichskette aufgenommen), auf das Variablen in JavaScript folgen.

        a = "global";
         function outer(){ 
              b = "local";
              console.log(a+b); //"globallocal"
         }
outer();

Umfangskette ->

  1. Fensterebene - aund outerFunktion befinden sich in der Bereichskette auf oberster Ebene.
  2. Wenn die äußere Funktion eine neue variable scope object(und in der Bereichskette enthaltene) Funktion mit einer darin enthaltenen Variablen baufruft.

Wenn eine Variable aerforderlich ist, sucht sie zuerst nach dem nächsten Variablenbereich und wenn die Variable nicht vorhanden ist, wird sie zum nächsten Objekt der Variablenbereichskette verschoben. In diesem Fall handelt es sich um die Fensterebene.


1
Ich bin mir nicht sicher, warum dies nicht die akzeptierte Antwort ist. Es gibt eigentlich nur Funktionsumfang (vor ECMA6 gab es keinen "lokalen Bereich") und globale Bindungen
Texasbruce

9

Um die anderen Antworten zu ergänzen, ist scope eine Nachschlageliste aller deklarierten Bezeichner (Variablen) und erzwingt strenge Regeln, wie diese für den aktuell ausgeführten Code zugänglich sind. Diese Suche kann zum Zuweisen der Variablen dienen, bei der es sich um eine LHS-Referenz (linke Seite) handelt, oder zum Abrufen ihres Werts, bei dem es sich um eine RHS-Referenz (rechte Seite) handelt. Diese Suchvorgänge werden von der JavaScript-Engine intern ausgeführt, wenn der Code kompiliert und ausgeführt wird.

Aus dieser Perspektive denke ich, dass ein Bild helfen würde, das ich im E-Book Scopes and Closures von Kyle Simpson gefunden habe:

Bild

Zitat aus seinem E-Book:

Das Gebäude repräsentiert den verschachtelten Regelsatz unseres Programms. Die erste Etage des Gebäudes repräsentiert Ihren aktuell ausgeführten Bereich, wo immer Sie sich befinden. Die oberste Ebene des Gebäudes ist die globale Reichweite. Sie lösen LHS- und RHS-Referenzen auf, indem Sie auf Ihre aktuelle Etage schauen. Wenn Sie sie nicht finden, fahren Sie mit dem Aufzug in die nächste Etage, suchen dort, dann in die nächste und so weiter. Sobald Sie die oberste Etage (den globalen Bereich) erreicht haben, finden Sie entweder das, wonach Sie suchen, oder Sie finden es nicht. Aber du musst trotzdem aufhören.

Eine erwähnenswerte Sache: "Die Scope-Suche wird beendet, sobald die erste Übereinstimmung gefunden wurde."

Diese Idee von "Bereichsebenen" erklärt, warum "dies" mit einem neu erstellten Bereich geändert werden kann, wenn er in einer verschachtelten Funktion nachgeschlagen wird. Hier ist ein Link, der auf all diese Details eingeht. Alles , was Sie über den Umfang von Javascript wissen wollten


8

Führen Sie den Code aus. Ich hoffe, dies gibt eine Vorstellung vom Scoping

Name = 'global data';
document.Name = 'current document data';
(function(window,document){
var Name = 'local data';
var myObj = {
    Name: 'object data',
    f: function(){
        alert(this.Name);
    }
};

myObj.newFun = function(){
    alert(this.Name);
}

function testFun(){
    alert("Window Scope : " + window.Name + 
          "\nLocal Scope : " + Name + 
          "\nObject Scope : " + this.Name + 
          "\nCurrent document Scope : " + document.Name
         );
}


testFun.call(myObj);
})(window,document);

8

Globaler Geltungsbereich:

Globale Variablen sind genau wie globale Sterne (Jackie Chan, Nelson Mandela). Sie können von jedem Teil Ihrer Anwendung aus darauf zugreifen (den Wert abrufen oder festlegen). Globale Funktionen sind wie globale Ereignisse (Neujahr, Weihnachten). Sie können sie von jedem Teil Ihrer Anwendung aus ausführen (aufrufen).

//global variable
var a = 2;

//global function
function b(){
   console.log(a);  //access global variable
}

Lokaler Geltungsbereich:

Wenn Sie in den USA sind, kennen Sie vielleicht Kim Kardashian, eine berüchtigte Berühmtheit (sie schafft es irgendwie, die Boulevardzeitungen zu machen). Aber Menschen außerhalb der USA werden sie nicht erkennen. Sie ist ein lokaler Star, der an ihr Territorium gebunden ist.

Lokale Variablen sind wie lokale Sterne. Sie können nur innerhalb des Bereichs auf sie zugreifen (den Wert abrufen oder festlegen). Eine lokale Funktion ist wie lokale Ereignisse - Sie können nur innerhalb dieses Bereichs ausführen (feiern). Wenn Sie von außerhalb des Bereichs darauf zugreifen möchten, wird ein Referenzfehler angezeigt

function b(){
   var d = 21; //local variable
   console.log(d);

   function dog(){  console.log(a); }
     dog(); //execute local function
}

 console.log(d); //ReferenceError: dddddd is not defined    

In diesem Artikel finden Sie detaillierte Informationen zum Umfang


6

Es gibt fast nur zwei Arten von JavaScript-Bereichen:

  • Der Umfang jeder var-Deklaration ist der am unmittelbarsten umschließenden Funktion zugeordnet
  • Wenn für eine var-Deklaration keine einschließende Funktion vorhanden ist, handelt es sich um einen globalen Bereich

Andere Blöcke als Funktionen erstellen also keinen neuen Bereich. Dies erklärt, warum for-Schleifen Variablen mit äußerem Gültigkeitsbereich überschreiben:

var i = 10, v = 10;
for (var i = 0; i < 5; i++) { var v = 5; }
console.log(i, v);
// output 5 5

Verwenden Sie stattdessen Funktionen:

var i = 10, v = 10;
$.each([0, 1, 2, 3, 4], function(i) { var v = 5; });
console.log(i,v);
// output 10 10

Im ersten Beispiel gab es keinen Blockbereich, sodass die ursprünglich deklarierten Variablen überschrieben wurden. Im zweiten Beispiel gab es aufgrund der Funktion einen neuen Bereich, sodass die ursprünglich deklarierten Variablen SHADOWED und nicht überschrieben wurden.

Das ist fast alles, was Sie in Bezug auf das JavaScript-Scoping wissen müssen, außer:

Sie können also sehen, dass das Scoping von JavaScript extrem einfach ist, wenn auch nicht immer intuitiv. Einige Dinge, die Sie beachten sollten:

  • var-Deklarationen werden an die Spitze des Bereichs gehoben. Dies bedeutet, dass es für den Compiler unabhängig davon, wo die var-Deklaration stattfindet, so ist, als ob die var selbst oben steht
  • Mehrere var-Deklarationen innerhalb desselben Bereichs werden kombiniert

Also dieser Code:

var i = 1;
function abc() {
  i = 2;
  var i = 3;
}
console.log(i);     // outputs 1

ist äquivalent zu:

var i = 1;
function abc() {
  var i;     // var declaration moved to the top of the scope
  i = 2;
  i = 3;     // the assignment stays where it is
}
console.log(i);

Dies mag kontraintuitiv erscheinen, ist jedoch aus der Sicht eines imperativen Sprachdesigners sinnvoll.


5

Moderne Js, ES6 +, ' const' und 'let '

Sie sollten Block Scoping für jede von Ihnen erstellte Variable verwenden, genau wie die meisten anderen Hauptsprachen. varist veraltet . Dies macht Ihren Code sicherer und wartbarer.

constsollte in 95% der Fälle verwendet werden . Das macht es so die variable Referenz kann nicht geändert werden . Die Eigenschaften von Array-, Objekt- und DOM-Knoten können sich ändern und sollten es wahrscheinlich sein const.

letsollte für jede Variable verwendet werden, die eine Neuzuweisung erwartet. Dies schließt innerhalb einer for-Schleife ein. Wenn Sie den Wert über die Initialisierung hinaus ändern, verwenden Sielet .

Blockbereich bedeutet, dass die Variable nur in den Klammern verfügbar ist, in denen sie deklariert ist. Dies gilt auch für interne Bereiche, einschließlich anonymer Funktionen, die in Ihrem Bereich erstellt wurden.


3

Versuchen Sie dieses merkwürdige Beispiel. Wenn im folgenden Beispiel a eine bei 0 initialisierte Zahl wäre, würden Sie 0 und dann 1 sehen. Außer a ist ein Objekt und Javascript übergibt f1 einen Zeiger von a und keine Kopie davon. Das Ergebnis ist, dass Sie beide Male dieselbe Warnung erhalten.

var a = new Date();
function f1(b)
{
    b.setDate(b.getDate()+1);
    alert(b.getDate());
}
f1(a);
alert(a.getDate());

3

In JS gibt es nur Funktionsbereiche. Bereiche nicht blockieren! Sie können sehen, was auch hebt.

var global_variable = "global_variable";
var hoisting_variable = "global_hoist";

// Global variables printed
console.log("global_scope: - global_variable: " + global_variable);
console.log("global_scope: - hoisting_variable: " + hoisting_variable);

if (true) {
    // The variable block will be global, on true condition.
    var block = "block";
}
console.log("global_scope: - block: " + block);

function local_function() {
    var local_variable = "local_variable";
    console.log("local_scope: - local_variable: " + local_variable);
    console.log("local_scope: - global_variable: " + global_variable);
    console.log("local_scope: - block: " + block);
    // The hoisting_variable is undefined at the moment.
    console.log("local_scope: - hoisting_variable: " + hoisting_variable);

    var hoisting_variable = "local_hoist";
    // The hoisting_variable is now set as a local one.
    console.log("local_scope: - hoisting_variable: " + hoisting_variable);
}

local_function();

// No variable in a separate function is visible into the global scope.
console.log("global_scope: - local_variable: " + local_variable);

(lange her, seit die Antwort veröffentlicht wurde) Bereich blockieren; developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Bob

2

Mein Verständnis ist, dass es drei Bereiche gibt: globaler Bereich, global verfügbar; lokaler Bereich, der für eine gesamte Funktion unabhängig von Blöcken verfügbar ist; und Blockbereich, der nur für den Block, die Anweisung oder den Ausdruck verfügbar ist, für den er verwendet wurde. Der globale und lokale Bereich wird entweder innerhalb oder außerhalb einer Funktion mit dem Schlüsselwort 'var' angegeben, und der Blockbereich wird mit dem Schlüsselwort 'let' angegeben.

Für diejenigen, die glauben, dass es nur einen globalen und lokalen Bereich gibt, erklären Sie bitte, warum Mozilla eine ganze Seite haben würde, die die Nuancen des Blockbereichs in JS beschreibt.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let


2

Ein sehr häufiges Problem, auf das Front-End-Codierer noch nicht häufig stoßen, ist der Bereich, der für einen Inline-Ereignishandler im HTML-Code sichtbar ist - beispielsweise mit

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

Der Umfang der Variablen, auf die ein on*Attribut verweisen kann, muss entweder sein:

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

Andernfalls erhalten Sie einen ReferenceError, wenn der Handler aufgerufen wird. Wenn der Inline-Handler beispielsweise auf eine Funktion verweist, die in window.onload oder definiert ist, schlägt$(function() { die Referenz fehl, da der Inline-Handler möglicherweise nur auf Variablen im globalen Bereich verweist und die Funktion nicht global ist:

Eigenschaften der documentund Eigenschaften des Elements der Handler angebracht sind kann auch als Standalone - Variablen innerhalb von Inline - Handler referenziert werden , da Inline - Handler aufgerufen werden , innerhalb von zwei withBlöcken , einer für das document, eine für das Element. Die Gültigkeitsbereichskette von Variablen in diesen Handlern ist äußerst unintuitiv , und ein Handler für Arbeitsereignisse erfordert wahrscheinlich eine globale Funktion (und unnötige globale Verschmutzung sollte wahrscheinlich vermieden werden ).

Da die Scope-Kette in Inline-Handlern so seltsam ist und Inline-Handler eine globale Verschmutzung benötigen, um zu funktionieren, und Inline-Handler manchmal hässliche Zeichenfolgen erfordern, die beim Übergeben von Argumenten entkommen, ist es wahrscheinlich einfacher, sie zu vermeiden. Fügen Sie stattdessen Ereignishandler mit Javascript (wie mit addEventListener) und nicht mit HTML-Markup hinzu.


Anders als bei normalen <script>Tags, die auf der obersten Ebene ausgeführt werden, wird Code in ES6-Modulen in einem eigenen privaten Bereich ausgeführt. Eine Variable, die oben in einem normalen <script>Tag definiert ist, ist global, sodass Sie sie in anderen <script>Tags wie folgt referenzieren können :

Die oberste Ebene eines ES6-Moduls ist jedoch nicht global. Eine Variable, die oben in einem ES6-Modul deklariert ist, ist nur in diesem Modul sichtbar, es sei denn, die Variable ist explizit exportbearbeitet oder einer Eigenschaft des globalen Objekts zugewiesen.

Die oberste Ebene eines ES6-Moduls ähnelt der des Inneren eines IIFE auf der obersten Ebene eines Normalen <script>. Das Modul kann auf alle Variablen verweisen, die global sind, und nichts kann auf irgendetwas innerhalb des Moduls verweisen, es sei denn, das Modul ist explizit dafür ausgelegt.


1

In JavaScript gibt es zwei Arten von Gültigkeitsbereichen:

  • Lokaler Bereich
  • Globaler Geltungsbereich

Die Funktion "Unten" verfügt über eine lokale Bereichsvariable carName. Und auf diese Variable kann außerhalb der Funktion nicht zugegriffen werden.

function myFunction() {
    var carName = "Volvo";
    alert(carName);
    // code here can use carName
}

Die unten stehende Klasse verfügt über eine globale Bereichsvariable carName. Und auf diese Variable kann von überall in der Klasse zugegriffen werden.

class {

    var carName = " Volvo";

    // code here can use carName

    function myFunction() {
        alert(carName);
        // code here can use carName 
    }
}

1

ES5 und früher:

Variablen in Javascript waren anfangs (vor ES6) lexikalisch funktionsbezogen. Der Begriff "lexikalisch" bedeutet, dass Sie den Umfang der Variablen sehen können, indem Sie den Code "betrachten".

Jede mit dem varSchlüsselwort deklarierte Variable ist der Funktion zugeordnet. Wenn jedoch andere Funktionen innerhalb dieser Funktion deklariert sind, haben diese Funktionen Zugriff auf die Variablen der äußeren Funktionen. Dies wird als Scope-Kette bezeichnet . Es funktioniert folgendermaßen:

  1. Wenn eine Funktion einen Variablenwert auflösen möchte, prüft sie zunächst ihren eigenen Bereich. Dies ist der Funktionskörper, dh alles zwischen geschweiften Klammern {} (mit Ausnahme von Variablen in anderen Funktionen, die in diesem Bereich liegen).
  2. Wenn die Variable im Funktionskörper nicht gefunden werden kann , klettert sie zur Kette und betrachtet den Variablenbereich in der Funktion, in der die Funktion definiert wurde . Dies ist mit lexikalischem Bereich gemeint. Wir können im Code sehen, wo diese Funktion definiert wurde, und können somit die Bereichskette bestimmen, indem wir nur den Code betrachten.

Beispiel:

// global scope
var foo = 'global';
var bar = 'global';
var foobar = 'global';

function outerFunc () {
 // outerFunc scope
 var foo = 'outerFunc';
 var foobar = 'outerFunc';
 innerFunc();
 
 function innerFunc(){
 // innerFunc scope
  var foo = 'innerFunc';
  console.log(foo);
  console.log(bar);
  console.log(foobar);
  }
}

outerFunc();

Was passiert, wenn wir versuchen, die Variablen und in der Konsole zu protokollieren foo, ist Folgendes:barfoobar

  1. Wir versuchen, foo in der Konsole zu protokollieren. Foo befindet sich in der Funktion innerFuncselbst. Daher wird der Wert von foo in die Zeichenfolge aufgelöst innerFunc.
  2. Wir versuchen, die Leiste in der Konsole zu protokollieren. Die Leiste befindet sich nicht in der Funktion innerFuncselbst. Daher müssen wir die Zielfernrohrkette erklimmen . Wir schauen uns zuerst die äußere Funktion an, in der die Funktion innerFuncdefiniert wurde. Dies ist die Funktion outerFunc. Im Rahmen von finden outerFuncwir die Variablenleiste, die die Zeichenfolge 'OuterFunc' enthält.
  3. foobar kann in innerFunc nicht gefunden werden. . Daher müssen wir die Scope-Kette zum innerFunc-Scope hochklettern. Es kann auch hier nicht gefunden werden, wir klettern eine andere Ebene zum globalen Bereich (dh zum äußersten Bereich). Wir finden hier die Variable foobar, die die Zeichenfolge 'global' enthält. Wenn die Variable nach dem Klettern in der Scope-Kette nicht gefunden worden wäre, würde die JS-Engine einen referenceError auslösen .

ES6 (ES 2015) und älter:

Die gleichen Konzepte von lexikalischem Umfang und Scopechain gelten immer noch in ES6. Es wurden jedoch neue Möglichkeiten zur Deklaration von Variablen eingeführt. Es gibt Folgendes:

  • let: Erstellt eine Variable mit Blockbereich
  • const: Erstellt eine Variable mit Blockbereich, die initialisiert werden muss und nicht neu zugewiesen werden kann

Der größte Unterschied zwischen varund let/ constbesteht darin, dass vares sich um einen Funktionsbereich handelt, während let/ einen constBlockbereich hat. Hier ist ein Beispiel, um dies zu veranschaulichen:

let letVar = 'global';
var varVar = 'global';

function foo () {
  
  if (true) {
    // this variable declared with let is scoped to the if block, block scoped
    let letVar = 5;
    // this variable declared with let is scoped to the function block, function scoped
    var varVar = 10;
  }
  
  console.log(letVar);
  console.log(varVar);
}


foo();

Im obigen Beispiel protokolliert letVar den Wert global, da mit deklarierte Variablen einen letBlockbereich haben. Sie existieren nicht mehr außerhalb ihres jeweiligen Blocks, sodass auf die Variable außerhalb des if-Blocks nicht zugegriffen werden kann.


0

In EcmaScript5 gibt es hauptsächlich zwei Bereiche, den lokalen Bereich und den globalen Bereich. In EcmaScript6 gibt es hauptsächlich drei Bereiche, den lokalen Bereich, den globalen Bereich und einen neuen Bereich, der als Blockbereich bezeichnet wird .

Beispiel für den Blockumfang ist: -

for ( let i = 0; i < 10; i++)
{
 statement1...
statement2...// inside this scope we can access the value of i, if we want to access the value of i outside for loop it will give undefined.
}

0

ECMAScript 6 führte die Schlüsselwörter let und const ein. Diese Schlüsselwörter können anstelle des Schlüsselworts var verwendet werden. Im Gegensatz zum Schlüsselwort var unterstützen die Schlüsselwörter let und const die Deklaration des lokalen Bereichs innerhalb von Blockanweisungen.

var x = 10
let y = 10
const z = 10
{
  x = 20
  let y = 20
  const z = 20
  {
    x = 30
    // x is in the global scope because of the 'var' keyword
    let y = 30
    // y is in the local scope because of the 'let' keyword
    const z = 30
    // z is in the local scope because of the 'const' keyword
    console.log(x) // 30
    console.log(y) // 30
    console.log(z) // 30
  }
  console.log(x) // 30
  console.log(y) // 20
  console.log(z) // 20
}

console.log(x) // 30
console.log(y) // 10
console.log(z) // 10

0

Die akzeptierte Antwort gefällt mir sehr gut, aber ich möchte Folgendes hinzufügen:

Scope sammelt und verwaltet eine Nachschlageliste aller deklarierten Bezeichner (Variablen) und erzwingt strenge Regeln, wie diese für aktuell ausgeführten Code zugänglich sind.

Der Bereich besteht aus einer Reihe von Regeln zum Nachschlagen von Variablen anhand ihres Bezeichnernamens.

  • Wenn eine Variable im unmittelbaren Bereich nicht gefunden werden kann, konsultiert Engine den nächsten äußeren enthaltenden Bereich und fährt fort, bis er gefunden wurde oder bis der äußerste (auch als global bezeichnete) Bereich erreicht wurde.
  • Ist das Regelwerk, das bestimmt, wo und wie eine Variable (Bezeichner) nachgeschlagen werden kann. Diese Suche kann zum Zuweisen der Variablen dienen, bei der es sich um eine LHS-Referenz (linke Seite) handelt, oder zum Abrufen ihres Werts, bei dem es sich um eine RHS-Referenz (rechte Seite) handelt .
  • LHS-Referenzen ergeben sich aus Zuweisungsoperationen. Bereichsbezogene Zuweisungen können entweder mit dem Operator = oder durch Übergeben von Argumenten an Funktionsparameter (Zuweisen zu) erfolgen.
  • Die JavaScript-Engine kompiliert zuerst Code, bevor sie ausgeführt wird, und teilt dabei Anweisungen wie var a = 2 auf. in zwei getrennte Schritte: 1 .. Zuerst var a, um es in diesem Bereich zu deklarieren. Dies wird zu Beginn vor der Codeausführung durchgeführt. 2 .. Später ist a = 2, um die Variable (LHS-Referenz) nachzuschlagen und ihr zuzuweisen, wenn sie gefunden wird.
  • Sowohl LHS- als auch RHS-Referenzsuchen beginnen mit dem aktuell ausgeführten Bereich und arbeiten sich bei Bedarf (dh sie finden dort nicht das, wonach sie suchen) im verschachtelten Bereich, einem Bereich (Etage) nach oben ) zu einem Zeitpunkt nach der Kennung suchen, bis sie in die globale (oberste Etage) gelangen und anhalten und sie entweder finden oder nicht. Nicht erfüllte RHS-Referenzen führen dazu, dass ReferenceError ausgelöst wird. Nicht erfüllte LHS-Referenzen führen zu einem automatisch implizit erstellten Global mit diesem Namen (wenn nicht im strengen Modus) oder einem ReferenceError (wenn nicht im strengen Modus).
  • Der Gültigkeitsbereich besteht aus einer Reihe von „Blasen“, die jeweils als Container oder Bucket fungieren und in denen Bezeichner (Variablen, Funktionen) deklariert sind. Diese Blasen verschachteln sauber ineinander, und diese Verschachtelung wird zum Zeitpunkt des Autors definiert.

-3

In JavaScript gibt es zwei Arten von Bereichen.

  1. Globaler Bereich : Variable, die im globalen Bereich angekündigt wird, kann überall im Programm sehr reibungslos verwendet werden. Zum Beispiel:

    var carName = " BMW";
    
    // code here can use carName
    
    function myFunction() {
         // code here can use carName 
    }
  2. Funktionsbereich oder lokaler Bereich : Die in diesem Bereich deklarierte Variable kann nur in ihrer eigenen Funktion verwendet werden. Zum Beispiel:

    // code here can not use carName
    function myFunction() {
       var carName = "BMW";
       // code here can use carName
    }
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.