Frage Was ist der Umfang der Variablen in JavaScript?


Was ist der Umfang der Variablen in Javascript? Haben sie den gleichen Bereich innerhalb wie außerhalb einer Funktion? Oder ist es überhaupt wichtig? Wo werden die Variablen gespeichert, wenn sie global definiert sind?


1711
2018-02-01 08:27


Ursprung


Antworten:


Ich denke, das Beste, was ich tun kann, ist eine Reihe von Beispielen zu studieren. JavaScript-Programmierer werden praktisch nach ihrem Umfang eingestuft. Es kann manchmal ziemlich kontraintuitiv sein.

  1. Eine global gültige Variable

    // global scope
    var a = 1;
    
    function one() {
      alert(a); // alerts '1'
    }
    
  2. Lokaler Umfang

    // global scope
    var a = 1;
    
    function two(a) {
      // local scope
      alert(a); // alerts the given argument, not the global value of '1'
    }
    
    // local scope again
    function three() {
      var a = 3;
      alert(a); // alerts '3'
    }
    
  3. Mittlere: In JavaScript gibt es keinen Blockbereich (ES5; ES6 führt ein let)

    ein.

    var a = 1;
    
    function four() {
      if (true) {
        var a = 4;
      }
    
      alert(a); // alerts '4', not the global value of '1'
    }
    

    b.

    var a = 1;
    
    function one() {
      if (true) {
        let a = 4;
      }
    
      alert(a); // alerts '1' because the 'let' keyword uses block scoping
    }
    
  4. Mittlere: Objekteigenschaften

    var a = 1;
    
    function Five() {
      this.a = 5;
    }
    
    alert(new Five().a); // alerts '5'
    
  5. Fortgeschritten: Schließung

    var a = 1;
    
    var six = (function() {
      var a = 6;
    
      return function() {
        // JavaScript "closure" means I have access to 'a' in here,
        // because it is defined in the function in which I was defined.
        alert(a); // alerts '6'
      };
    })();
    
  6. Fortgeschritten: Prototypbasierte Bereichsauflösung

    var a = 1;
    
    function seven() {
      this.a = 7;
    }
    
    // [object].prototype.property loses to
    // [object].property in the lookup chain. For example...
    
    // Won't get reached, because 'a' is set in the constructor above.
    seven.prototype.a = -1;
    
    // Will get reached, even though 'b' is NOT set in the constructor.
    seven.prototype.b = 8;
    
    alert(new seven().a); // alerts '7'
    alert(new seven().b); // alerts '8'
    

  7. Global + Lokal: Ein extra komplexer Fall

    var x = 5;
    
    (function () {
        console.log(x);
        var x = 10;
        console.log(x); 
    })();
    

    Dies wird ausgedruckt undefined und 10 eher, als 5 und 10 Da JavaScript Variablendeklarationen (nicht Initialisierungen) immer an den Anfang des Bereichs verschiebt, entspricht der Code:

    var x = 5;
    
    (function () {
        var x;
        console.log(x);
        x = 10;
        console.log(x); 
    })();
    
  8. Catch-Klausel-Variable

    var e = 5;
    console.log(e);
    try {
        throw 6;
    } catch (e) {
        console.log(e);
    }
    console.log(e);
    

    Dies wird ausgedruckt 5, 6, 5. Innerhalb der Fangklausel e Schattiert globale und lokale Variablen. Aber dieser spezielle Bereich gilt nur für die gefangene Variable. Wenn du schreibst var f; Innerhalb der catch-Klausel ist es genau so, als ob Sie es vor oder nach dem try-catch-Block definiert hätten.


2262
2018-02-01 08:58



JavaScript verwendet Bereichsketten, um den Bereich für eine bestimmte Funktion festzulegen. Es gibt normalerweise einen globalen Bereich, und jede definierte Funktion hat ihren 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 Scope-Kette ist im Grunde eine Map mit einem Zeiger auf den übergeordneten Bereich.

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


219
2018-02-01 08:35



Global deklarierte Variablen haben einen globalen Gültigkeitsbereich. Variablen, die innerhalb einer Funktion deklariert sind, sind auf diese Funktion beschränkt und globale Variablen gleichen Namens schattieren.

(Ich bin mir sicher, dass es viele Feinheiten gibt, auf die echte JavaScript-Programmierer in anderen Antworten hinweisen können. Insbesondere bin ich darauf gestoßen diese Seite worüber genau this bedeutet zu jeder Zeit. hoffnungsvoll dieser einleitende Link ist genug, um Sie zu beginnen, obwohl.)


93
2018-02-01 08:31



Alte Schule JavaScript

Traditionell hat JavaScript nur zwei Arten von Geltungsbereich:

  1. Globaler Geltungsbereich : Variablen sind in der gesamten Anwendung vom Anfang der Anwendung bekannt (*)
  2. Funktionsumfang : Variablen sind innerhalb bekannt die Funktion Sie sind ab dem Start der Funktion deklariert (*)

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


Modernes JavaScript

Das neueste JavaScript-Spezifikationen erlaube jetzt auch einen dritten Bereich:

  1. Bereich blockieren : Variablen sind innerhalb bekannt der BlockSie sind ab dem Zeitpunkt der Anmeldung angemeldet (**)

Wie erstelle ich Blockbereichsvariablen?

Traditionell erstellen Sie Ihre Variablen wie folgt:

var myVariable = "Some text";

Block-Scope-Variablen werden wie folgt erstellt:

let myVariable = "Some text";

Worin besteht also der Unterschied zwischen Funktionsumfang und Blockumfang?

Beachten Sie den folgenden Code, um den Unterschied zwischen Funktionsumfang und Blockbereich 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 j ist nur in der ersten for-Schleife bekannt, aber nicht davor und danach. Aber unsere Variable i ist in der gesamten Funktion bekannt.

Beachten Sie außerdem, dass Blockbereichsvariablen nicht bekannt sind, bevor sie deklariert werden, da sie nicht gehisst sind. Es ist Ihnen auch nicht erlaubt, dieselbe Block-Bereichsvariable innerhalb desselben Blocks neu zu deklarieren. Dies macht Block-Scoped-Variablen weniger fehleranfällig als Global- oder Funktions-Scoped-Variablen, die gehisst werden und die bei mehreren Deklarationen keine Fehler erzeugen.


Ist es heute sicher, Block-Scope-Variablen zu verwenden?

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

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

  • Wenn Sie clientseitigen JavaScript-Code schreiben und einen transpiler (wie Traceur), können Sie sicher das verwenden let Allerdings ist Ihr Code in Bezug auf die Leistung wahrscheinlich alles andere als optimal.

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

    Heute, 23. Februar 2016, sind dies einige Browser, die dies nicht unterstützen let oder haben nur teilweise Unterstützung:

    • Internet Explorer 10 und unten (keine Unterstützung)
    • Firefox 43 und unten (keine Unterstützung)
    • Safari 9 und unten (keine Unterstützung)
    • Opera Mini 8 und unten (keine Unterstützung)
    • Android-Browser 4 und unten (keine Unterstützung)
    • Oper 36 und unten (teilweise Unterstützung)
    • Chome 51 und unten (teilweise Unterstützung)

enter image description here


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

Für einen aktuellen Überblick darüber, welche Browser den Browser unterstützen let Aussage zum Zeitpunkt der Lektüre dieser Antwort, siehe Dies Can I Use Seite.


(*) Global und funktional beschränkte Variablen können initialisiert und verwendet werden, bevor sie deklariert werden, weil JavaScript Variablen sind gehisst. Dies bedeutet, dass Deklarationen immer ganz oben stehen.

(**) Block-Bereichsvariablen werden nicht gehisst


55
2018-02-23 18:51



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 wollen Schließungen untersuchen und herausfinden, wie Sie sie verwenden können private Mitglieder.


35
2018-02-01 08:48



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

Hier ist ein guter Artikel zum Thema.


28
2018-05-15 17:38



In "Javascript 1.7" (Mozilla-Erweiterung zu Javascript) kann man auch Block-Scope-Variablen mit deklarieren let Erklärung:

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

23
2018-04-06 11:19



Die Idee des Scoping in JavaScript, als es ursprünglich von Brendan Eich kam von der HyperCard Skriptsprache HyperTalk.

In dieser Sprache wurden die Anzeigen ähnlich einem Stapel von Karteikarten ausgeführt. Es gab eine Masterkarte, die als Hintergrund bezeichnet wurde. Es war transparent und kann als die untere Karte gesehen werden. Alle Inhalte auf dieser Basiskarte wurden mit Karten geteilt, die darüber gelegt wurden. Jede Karte, die oben platziert wurde, hatte ihren eigenen Inhalt, der Vorrang vor der vorherigen Karte hatte, hatte aber immer noch Zugriff auf die vorherigen Karten, falls gewünscht.

Genau so ist das JavaScript-Scoping-System konzipiert. Es hat nur andere Namen. Die Karten in JavaScript sind bekannt als AusführungskontexteECMA. Jeder dieser Kontexte enthält drei Hauptteile. Eine variable Umgebung, eine lexikalische Umgebung und eine this-Bindung. Zurück zur Kartenreferenz enthält die lexikalische Umgebung den gesamten Inhalt von früheren Karten, die sich im Stapel befinden. Der aktuelle Kontext befindet sich an der Spitze des Stapels und alle darin deklarierten Inhalte werden in der Variablenumgebung gespeichert. Die Variablenumgebung hat Vorrang, wenn Kollisionen benannt werden.

Diese Bindung zeigt auf das umgebende Objekt. Manchmal ändern sich Bereiche oder Ausführungskontexte, ohne dass sich das enthaltene Objekt ändert, z. B. in einer deklarierten Funktion, in der das enthaltene Objekt enthalten sein kann window oder eine Konstruktorfunktion.

Diese Ausführungskontexte werden immer dann erstellt, wenn die Kontrolle übertragen wird. Die Steuerung wird übertragen, wenn der Code ausgeführt wird, und dies erfolgt hauptsächlich durch die Ausführung der Funktionen.

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

  • Bereiche sind technisch "Ausführungskontexte"
  • Kontexte bilden einen Stapel von Umgebungen, in denen Variablen gespeichert sind
  • Der obere Teil des Stapels hat Vorrang (der untere ist der globale Kontext)
  • Jede Funktion erstellt einen Ausführungskontext (aber nicht immer eine neue Bindung)

Wenn Sie dies auf eines der vorherigen Beispiele (5. "Closure") auf dieser Seite anwenden, ist es möglich, dem Stapel von Ausführungskontexten zu folgen. In diesem Beispiel gibt es 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 unmittelbar aufgerufenen Funktion von var six definiert.

ich) 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 Stack Vorrang hat
iii) Der zurückgegebene Funktionskontext hat eine lexikalische Umgebung von a = 6 und das ist der Wert, auf den beim Aufruf der Warnung verwiesen wird.

enter image description here


18
2017-09-14 20:29



1) Es gibt einen globalen Bereich, einen Funktionsumfang und die Bereiche mit und mit Fang. Es gibt im Allgemeinen keinen 'Block'-Ebenenbereich für Variablen - die With- und die Catch-Anweisungen fügen ihren Blöcken Namen hinzu.

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

3) Eigenschaften werden aufgelöst, indem man die Prototypkette durchgeht. Die with-Anweisung bringt Objekteigenschaftsnamen in den lexikalischen Bereich, der durch den with-Block definiert wird.

BEARBEITEN: ECMAAScript 6 (Harmony) ist auf Unterstützung eingestellt, und ich weiß, dass Chrome eine "Harmony" -Flagge erlaubt, also vielleicht unterstützt es es.

Lassen Sie das Block-Level-Scoping unterstützen, aber Sie müssen das Schlüsselwort verwenden, um dies zu ermöglichen.

EDIT: Basierend auf Benjamins Hinweis auf die With & Catch-Anweisungen in den Kommentaren habe ich den Beitrag bearbeitet und mehr hinzugefügt. Sowohl die With- als auch die Catch-Anweisung führen Variablen in ihre jeweiligen Blöcke ein ist ein Blockumfang. Diese Variablen sind als Alias ​​für die Eigenschaften der Objekte definiert, die an sie übergeben werden.

 //chrome (v8)

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

EDIT: Erklärungsbeispiel:

test1 ist auf den with-Block beschränkt, wird jedoch mit a.test1 abgeglichen. '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 'mit' - genau wie var ein Noop ist, wenn die Variable bereits in der Funktion definiert ist, ist es auch ein Noop in Bezug auf die aus dem Objekt importierten Namen! Ein wenig Heads-Up über den bereits definierten Namen würde dies viel sicherer machen. Ich persönlich werde nie damit arbeiten.


16
2017-10-25 00:41



Ich habe festgestellt, dass viele JavaScript-Neulinge Probleme beim Verständnis haben, dass die Vererbung standardmäßig in der Sprache verfügbar ist und dass der Funktionsumfang bisher der einzige Bereich ist. Ich habe einer Verschönerung, die ich Ende letzten Jahres geschrieben habe, eine Erweiterung namens JSPretty angeboten. Die Feature-Farben funktionieren im Code und ordnen allen Variablen, die in diesem Bereich deklariert sind, immer eine Farbe zu. Die Schließung wird visuell demonstriert, wenn eine Variable mit einer Farbe aus einem Bereich in einem anderen Bereich verwendet wird.

Probieren Sie das Feature aus:

Sehen Sie eine Demo unter:

Zeigen Sie den Code an:

Gegenwärtig bietet das Feature Unterstützung für eine Tiefe von 16 verschachtelten Funktionen, derzeit werden jedoch keine globalen Variablen eingefärbt.


9
2018-03-21 17:31