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?
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:
Global - für alles sichtbar
Funktion - sichtbar innerhalb einer Funktion (und ihrer Unterfunktionen und Blöcke)
Block - sichtbar innerhalb eines Blocks (und seiner Unterblöcke)
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:
Einige der Möglichkeiten, wie Bezeichner deklariert werden können:
var, letundconst
Funktionsparameter
Catch-Block-Parameter
Funktionsdeklarationen
Benannte Funktionsausdrücke
Implizit definierte Eigenschaften für das globale Objekt (dh varim nicht strengen Modus fehlen )
import Aussagen
eval
Einige der Standortkennungen können deklariert werden:
Globaler Kontext
Funktionskörper
Gewöhnlicher Block
Die Oberseite einer Kontrollstruktur (z. B. Schleife, wenn, während usw.)
Kontrollstrukturkörper
Module
Deklarationsstile
var
Mit deklarierte Bezeichner varhaben 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 consteinen Blockbereich haben , außer wenn sie direkt im globalen Kontext deklariert werden. In diesem Fall haben sie einen globalen Bereich.
Hinweis: let, constund varsind 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 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 =1let y =1const 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 =1let y =1const 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
... 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
Variablen, die mit oder für Module deklariert wurden var, sind alle auf Module beschränkt:letconst
// module1.jsvar x =0exportfunction f(){}//module2.jsimport 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
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
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.
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.
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 .)
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:
Globaler Geltungsbereich : Variablen sind in der gesamten Anwendung ab dem Start der Anwendung bekannt (*).
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.
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 herefunction 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 herefor(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 herefor(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
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.
"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.globalVariablefunction aGlobal( param ){//==window.aGlobal(); //param is only accessible in this functionvar 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 .
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.
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 definedwith(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.
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:
Globaler Bereich : Global ist nichts anderes als ein Bereich auf Fensterebene. Hier ist eine Variable in der gesamten Anwendung vorhanden.
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 ->
Fensterebene - aund outerFunktion befinden sich in der Bereichskette auf oberster Ebene.
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.
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:
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
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 variablevar a =2;//global functionfunction 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
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:
try / catch führt NUR für die Ausnahmevariable selbst einen neuen Bereich ein, andere Variablen haben keinen neuen Bereich
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.
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.
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 =newDate();function f1(b){
b.setDate(b.getDate()+1);
alert(b.getDate());}
f1(a);
alert(a.getDate());
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);
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.
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
<buttononclick="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 inwindow.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.
<scripttype="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 ä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.
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 carNamefunction myFunction(){
alert(carName);// code here can use carName }}
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:
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).
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.
Was passiert, wenn wir versuchen, die Variablen und in der Konsole zu protokollieren foo, ist Folgendes:barfoobar
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.
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.
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 scopedlet letVar =5;// this variable declared with let is scoped to the function block, function scopedvar 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.
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.}
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 =10let y =10const z =10{
x =20let y =20const z =20{
x =30// x is in the global scope because of the 'var' keywordlet y =30// y is in the local scope because of the 'let' keywordconst 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
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.
Antworten:
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:
Außerhalb der Sonderfälle des globalen Bereichs und des Modulbereichs werden Variablen mit
var
(Funktionsbereich),let
(Blockbereich) undconst
(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:
Einige der Möglichkeiten, wie Bezeichner deklariert werden können:
var
,let
undconst
var
im nicht strengen Modus fehlen )import
Aussageneval
Einige der Standortkennungen können deklariert werden:
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 ineval
Funktionen.let und const
Bezeichner, die mit deklariert wurden
let
undconst
einen Blockbereich haben , außer wenn sie direkt im globalen Kontext deklariert werden. In diesem Fall haben sie einen globalen Bereich.Hinweis:
let
,const
undvar
sind alle hochgezogen . Dies bedeutet, dass ihre logische Definitionsposition die Spitze ihres umschließenden Bereichs (Block oder Funktion) ist. Variablen, die als verwendet deklariert wurdenlet
undconst
erst gelesen oder zugewiesen werden können, wenn die Steuerung den Deklarationspunkt im Quellcode überschritten hat. Die Zwischenzeit wird als zeitliche Totzone bezeichnet.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
eval
Zeichenfolgen werden mit deklarierte Variablenvar
im aktuellen Bereich platziert oder, wenneval
sie indirekt verwendet werden, als Eigenschaften für das globale Objekt.Beispiele
Im Folgenden wird ein Reference weil die Namen werfen
x
,y
undz
haben keine Bedeutung außerhalb der Funktionf
.Im Folgenden wird ein ReferenceError für
y
und ausgelöstz
, jedoch nicht fürx
, da die Sichtbarkeit vonx
nicht durch den Block eingeschränkt wird. Blöcke, die die Körper von Kontrollstrukturen definieren möchtenif
,for
undwhile
, ähnlich verhalten.Im Folgenden
x
ist außerhalb der Schleife sichtbar, davar
Funktionsumfang hat:... wegen dieses Verhaltens müssen Sie vorsichtig sein, wenn Sie Variablen schließen, die mit
var
in Schleifen deklariert wurden . Hier ist nur eine Instanz einer Variablenx
deklariert, die sich logisch außerhalb der Schleife befindet.Die folgenden Drucke
5
werden fünfmal und dann5
zum sechsten Malconsole.log
außerhalb der Schleife gedruckt :Das Folgende wird gedruckt,
undefined
weilx
es einen Blockbereich hat. Die Rückrufe werden einzeln asynchron ausgeführt. Neues Verhalten fürlet
Variablen bedeutet, dass jede anonyme Funktion über einer anderen Variablen mit dem Namen geschlossen wirdx
(anders als dies der Fall gewesen wärevar
) und daher Ganzzahlen0
durchgedruckt4
werden:Im Folgenden wird KEIN a ausgelöst,
ReferenceError
da die Sichtbarkeit vonx
nicht durch den Block eingeschränkt wird. Es wird jedoch gedruckt,undefined
da die Variable nicht initialisiert wurde (aufgrund derif
Anweisung).Eine Variable, die am Anfang einer
for
Schleife mitlet
deklariert wird, ist auf den Hauptteil der Schleife beschränkt:Im Folgenden wird a ausgelöst,
ReferenceError
da die Sichtbarkeit vonx
durch den Block eingeschränkt wird:Variablen, die mit oder für Module deklariert wurden
var
, sind alle auf Module beschränkt:let
const
Im Folgenden wird eine Eigenschaft für das globale Objekt deklariert, da
var
im globalen Kontext deklarierte Variablen als Eigenschaften zum globalen Objekt hinzugefügt werden:let
undconst
fügen Sie im globalen Kontext keine Eigenschaften zum globalen Objekt hinzu, haben aber dennoch einen globalen Bereich:Funktionsparameter können als im Funktionskörper deklariert betrachtet werden:
Catch-Block-Parameter sind auf den Catch-Block-Body beschränkt:
Benannte Funktionsausdrücke beziehen sich nur auf den Ausdruck selbst:
Im nicht strengen Modus werden implizit definierte Eigenschaften für das globale Objekt global definiert. Im strengen Modus wird eine Fehlermeldung angezeigt.
Im nicht strengen Modus haben Funktionsdeklarationen einen Funktionsumfang. Im strengen Modus haben sie Blockbereich.
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 .
quelle
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.
quelle
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
this
zu jeder Zeit bedeutet. Hoffentlich reicht dieser einführende Link aus, um Ihnen den Einstieg zu erleichtern .)quelle
JavaScript der alten Schule
Traditionell hat JavaScript nur zwei Arten von Gültigkeitsbereichen:
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:
Wie erstelle ich Blockbereichsvariablen?
Traditionell erstellen Sie Ihre Variablen folgendermaßen:
Blockbereichsvariablen werden wie folgt erstellt:
Was ist also der Unterschied zwischen Funktionsumfang und Blockumfang?
Beachten Sie den folgenden Code, um den Unterschied zwischen Funktionsumfang und Blockumfang zu verstehen:
Hier können wir sehen, dass unsere Variable
j
nur in der ersten for-Schleife bekannt ist, nicht jedoch vorher und nachher. Unsere Variablei
ist 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
let
Anweisung sicher verwenden .Wenn Sie clientseitigen JavaScript-Code schreiben und einen browserbasierten Transpiler (wie Traceur oder babel-standalone ) verwenden, können Sie die
let
Anweisung 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
let
Anweisung 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
:So behalten Sie den Überblick über die Browserunterstützung
Auf dieser Seite finden Sie eine aktuelle Übersicht darüber, welche Browser die
let
Aussage 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.
quelle
Hier ist ein Beispiel:
Sie sollten die Schließungen untersuchen und untersuchen, wie Sie sie verwenden können, um private Mitglieder zu gewinnen .
quelle
Der Schlüssel, so wie ich es verstehe, ist, dass Javascript ein Scoping auf Funktionsebene im Vergleich zum häufigeren C-Block-Scoping hat.
Hier ist ein guter Artikel zu diesem Thema.
quelle
In "Javascript 1.7" (Mozillas Erweiterung zu Javascript) kann man auch Block-Scope-Variablen mit der folgenden
let
Anweisung deklarieren :quelle
let
.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,
window
oder 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
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.
quelle
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.
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.
quelle
with
Aussage ist aber eine Form des Block Scopingcatch
Klauseln sind eine viel häufigere Form (Fun fact, v8 implementiertcatch
mit awith
) - 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.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.
quelle
JavaScript hat nur zwei Arten von Gültigkeitsbereichen:
var
Schlü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.
Umfangskette ->
a
undouter
Funktion befinden sich in der Bereichskette auf oberster Ebene.variable scope object
(und in der Bereichskette enthaltene) Funktion mit einer darin enthaltenen Variablenb
aufruft.Wenn eine Variable
a
erforderlich 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.quelle
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:
Zitat aus seinem E-Book:
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
quelle
Führen Sie den Code aus. Ich hoffe, dies gibt eine Vorstellung vom Scoping
quelle
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).
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
In diesem Artikel finden Sie detaillierte Informationen zum Umfang
quelle
Es gibt fast nur zwei Arten von JavaScript-Bereichen:
Andere Blöcke als Funktionen erstellen also keinen neuen Bereich. Dies erklärt, warum for-Schleifen Variablen mit äußerem Gültigkeitsbereich überschreiben:
Verwenden Sie stattdessen Funktionen:
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:
Also dieser Code:
ist äquivalent zu:
Dies mag kontraintuitiv erscheinen, ist jedoch aus der Sicht eines imperativen Sprachdesigners sinnvoll.
quelle
Moderne Js, ES6 +, '
const
' und 'let
'Sie sollten Block Scoping für jede von Ihnen erstellte Variable verwenden, genau wie die meisten anderen Hauptsprachen.
var
ist veraltet . Dies macht Ihren Code sicherer und wartbarer.const
sollte 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 seinconst
.let
sollte 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.
quelle
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.
quelle
In JS gibt es nur Funktionsbereiche. Bereiche nicht blockieren! Sie können sehen, was auch hebt.
quelle
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
quelle
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
Der Umfang der Variablen, auf die ein
on*
Attribut verweisen kann, muss entweder sein:querySelector
B. als eigenständige Variable , auf die verwiesen wirddocument.querySelector
; 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:Code-Snippet anzeigen
Eigenschaften der
document
und 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 zweiwith
Blöcken , einer für dasdocument
, 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.Code-Snippet anzeigen
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 :Code-Snippet anzeigen
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
export
bearbeitet oder einer Eigenschaft des globalen Objekts zugewiesen.Code-Snippet anzeigen
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.quelle
In JavaScript gibt es zwei Arten von Gültigkeitsbereichen:
Die Funktion "Unten" verfügt über eine lokale Bereichsvariable
carName
. Und auf diese Variable kann außerhalb der Funktion nicht zugegriffen werden.Die unten stehende Klasse verfügt über eine globale Bereichsvariable
carName
. Und auf diese Variable kann von überall in der Klasse zugegriffen werden.quelle
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
var
Schlü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:Beispiel:
Was passiert, wenn wir versuchen, die Variablen und in der Konsole zu protokollieren
foo
, ist Folgendes:bar
foobar
innerFunc
selbst. Daher wird der Wert von foo in die Zeichenfolge aufgelöstinnerFunc
.innerFunc
selbst. Daher müssen wir die Zielfernrohrkette erklimmen . Wir schauen uns zuerst die äußere Funktion an, in der die FunktioninnerFunc
definiert wurde. Dies ist die FunktionouterFunc
. Im Rahmen von findenouterFunc
wir die Variablenleiste, die die Zeichenfolge 'OuterFunc' enthält.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 Blockbereichconst
: Erstellt eine Variable mit Blockbereich, die initialisiert werden muss und nicht neu zugewiesen werden kannDer größte Unterschied zwischen
var
undlet
/const
besteht darin, dassvar
es sich um einen Funktionsbereich handelt, währendlet
/ einenconst
Blockbereich hat. Hier ist ein Beispiel, um dies zu veranschaulichen:Im obigen Beispiel protokolliert letVar den Wert global, da mit deklarierte Variablen einen
let
Blockbereich haben. Sie existieren nicht mehr außerhalb ihres jeweiligen Blocks, sodass auf die Variable außerhalb des if-Blocks nicht zugegriffen werden kann.quelle
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: -
quelle
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.
quelle
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.
quelle
In JavaScript gibt es zwei Arten von Bereichen.
Globaler Bereich : Variable, die im globalen Bereich angekündigt wird, kann überall im Programm sehr reibungslos verwendet werden. Zum Beispiel:
Funktionsbereich oder lokaler Bereich : Die in diesem Bereich deklarierte Variable kann nur in ihrer eigenen Funktion verwendet werden. Zum Beispiel:
quelle