Unterschied zwischen Syntaxen für Variablendeklarationen in Javascript (einschließlich globaler Variablen)?

292

Gibt es einen Unterschied zwischen der Deklaration einer Variablen:

var a=0; //1

...diesen Weg:

a=0; //2

...oder:

window.a=0; //3

im globalen Geltungsbereich?

Dan
quelle
2
AFAIK var a = 0; funktioniert nicht im IE, wenn auf die Variable über eine andere externe js-Datei zugegriffen wird, die in einer anderen js-Datei deklariert ist
Aivan Monceller
Ich weiß nichts über window.a, aber die anderen beiden Möglichkeiten sind im globalen Bereich gleich.
Programmierer
1
@AivanMonceller wirklich? Link bitte.
Raynos
@ Raynos, ich erlebe es auf meiner eigenen Website. IE6 um genau zu sein. Ich konnte mein var enum nicht anzeigen lassen, das sich in einer externen js-Datei befindet, und ich verweise es als Inline-Javascript in einer HTML-Datei
Aivan Monceller
@Ashwini Im globalen Bereich ist Fenster das globale Objekt (in Browsern). var a = 1; console.log (a); console.log (win
leebriggs

Antworten:

557

Ja, es gibt ein paar Unterschiede, obwohl sie in der Praxis normalerweise nicht groß sind.

Es gibt einen vierten Weg und ab ES2015 (ES6) gibt es zwei weitere. Ich habe den vierten Weg am Ende hinzugefügt, aber die ES2015-Wege nach # 1 eingefügt (Sie werden sehen warum), also haben wir:

var a = 0;     // 1
let a = 0;     // 1.1 (new with ES2015)
const a = 0;   // 1.2 (new with ES2015)
a = 0;         // 2
window.a = 0;  // 3
this.a = 0;    // 4

Diese Aussagen erklärt

# 1 var a = 0;

Dadurch wird eine globale Variable erstellt, die auch eine Eigenschaft des globalen Objekts ist , auf das wir als zugreifenwindow in Browsern (oder über thiseinen globalen Bereich in nicht striktem Code). Im Gegensatz zu einigen anderen Eigenschaften kann die Eigenschaft nicht über entfernt werden delete.

In Spezifikation Bedingungen schafft sie eine Kennung Bindung an das Objekt Umgebungsdatensatz für die globale Umwelt . Dies macht es zu einer Eigenschaft des globalen Objekts, da im globalen Objekt Bezeichnerbindungen für den Objektumgebungsdatensatz der globalen Umgebung gespeichert sind. Aus diesem Grund kann die Eigenschaft nicht gelöscht werden: Es handelt sich nicht nur um eine einfache Eigenschaft, sondern um eine Bezeichnerbindung.

Die Bindung (Variable) wird definiert, bevor die erste Codezeile ausgeführt wird (siehe "Wann var passiert" weiter unten).

Beachten Sie, dass in IE8 und früheren Versionen die am erstellte Eigenschaft windownicht aufzählbar ist (nicht in angezeigt wird)for..in Anweisungen ). In IE9, Chrome, Firefox und Opera ist es aufzählbar.


# 1.1 let a = 0;

Dadurch wird eine globale Variable erstellt keine Eigenschaft des globalen Objekts ist. Dies ist eine neue Sache ab ES2015.

In Bezug auf die Spezifikation wird eine Bezeichnerbindung für den deklarativen Umgebungsdatensatz für die globale Umgebung und nicht für den Objektumgebungsdatensatz erstellt . Die globale Umwelt ist einzigartig eine geteilte Umwelt Record, eine für alle alten Sachen in aufweist, der (die auf das globale Objekt geht Objekt Environment Record) und eine für alle neuen Sachen ( let, constund die erstellten Funktionenclass ) , die nicht zu tun gehe auf das globale Objekt.

Die Bindung wird erstellt, bevor ein schrittweiser Code in seinem umschließenden Block ausgeführt wird (in diesem Fall, bevor ein globaler Code ausgeführt wird). Sie ist jedoch in keiner Weise zugänglich , bis die schrittweise Ausführung die letAnweisung erreicht. Sobald die Ausführung die letAnweisung erreicht hat, kann auf die Variable zugegriffen werden. (Siehe "Wann letundconst passiert" weiter unten.)


# 1.2 const a = 0;

Erstellt eine globale Konstante, die keine Eigenschaft des globalen Objekts ist.

constist genau so, letaußer dass Sie einen Initialisierer (das = valueTeil) bereitstellen müssen und den Wert der Konstante nach ihrer Erstellung nicht mehr ändern können. Unter der Decke ist es genau so, letaber mit einem Flag auf der Bezeichnerbindung, das besagt, dass sein Wert nicht geändert werden kann. Verwenden constmacht drei Dinge für Sie:

  1. Macht es zu einem Analysezeitfehler, wenn Sie versuchen, der Konstante zuzuweisen.
  2. Dokumentiert seine unveränderliche Natur für andere Programmierer.
  3. Lässt die JavaScript-Engine auf der Grundlage optimieren, dass sie sich nicht ändert.

# 2 a = 0;

Dadurch wird implizit eine Eigenschaft für das globale Objekt erstellt . Da es sich um eine normale Eigenschaft handelt, können Sie sie löschen. Ich würde empfehlen , dies nicht zu tun. Es kann für jeden, der Ihren Code später liest, unklar sein. Wenn Sie den strengen Modus von ES5 verwenden, ist dies ein Fehler (Zuweisen zu einer nicht vorhandenen Variablen). Dies ist einer von mehreren Gründen, den strengen Modus zu verwenden.

Interessanterweise wurde die in IE8 und früheren Versionen erstellte Eigenschaft nicht aufzählbar erstellt (wird in for..inAnweisungen nicht angezeigt ). Das ist seltsam, besonders angesichts der Nummer 3 unten.


#3 window.a = 0;

Dadurch wird explizit eine Eigenschaft für das globale Objekt erstellt, wobei die windowglobale Eigenschaft verwendet wird, die sich auf das globale Objekt bezieht (in Browsern; einige Nicht-Browser-Umgebungen verfügen über eine äquivalente globale Variable, z. B. globalin NodeJS). Da es sich um eine normale Eigenschaft handelt, können Sie sie löschen.

Diese Eigenschaft ist in IE8 und früheren Versionen sowie in jedem anderen Browser, den ich ausprobiert habe, aufzählbar.


# 4 this.a = 0;

Genau wie # 3, außer dass wir das globale Objekt thisanstelle des globalen Objekts referenzieren window. Dies funktioniert jedoch im strengen Modus nicht, da der globale Code im strengen Modus thiskeinen Verweis auf das globale Objekt hat ( undefinedstattdessen hat er den Wert ).


Eigenschaften löschen

Was meine ich mit "Löschen" oder "Entfernen" a? Genau das: Entfernen der Eigenschaft (vollständig) über das deleteSchlüsselwort:

window.a = 0;
display("'a' in window? " + ('a' in window)); // displays "true"
delete window.a;
display("'a' in window? " + ('a' in window)); // displays "false"

deleteEntfernt eine Eigenschaft vollständig aus einem Objekt. Sie können nicht tun, mit Eigenschaften hinzugefügt windowüber indirekt var, die deleteentweder stillschweigend ignoriert oder lösen eine Ausnahme (abhängig von der JavaScript - Implementierung und ob Sie im strikten Modus sind).

Warnung : IE8 erneut (und vermutlich früher, und IE9-IE11 im fehlerhaften "Kompatibilitäts" -Modus): Sie können die Eigenschaften des windowObjekts nicht löschen , selbst wenn dies zulässig sein sollte. Schlimmer noch, es wird eine Ausnahme ausgelöst, wenn Sie es versuchen ( versuchen Sie dieses Experiment in IE8 und in anderen Browsern). Wenn Sie also aus dem windowObjekt löschen , müssen Sie defensiv sein:

try {
    delete window.prop;
}
catch (e) {
    window.prop = undefined;
}

Dadurch wird versucht, die Eigenschaft zu löschen. Wenn eine Ausnahme ausgelöst wird, wird das nächstbeste ausgeführt und die Eigenschaft auf gesetzt undefined.

Dies gilt nur für das windowObjekt und (soweit ich weiß) nur für IE8 und früher (oder IE9-IE11 im fehlerhaften "Kompatibilitätsmodus"). Andere Browser können windownach den oben genannten Regeln problemlos Eigenschaften löschen .


Wann varpassiert

Die über die varAnweisung definierten Variablen werden erstellt, bevor ein schrittweiser Code im Ausführungskontext ausgeführt wird. Daher existiert die Eigenschaft weit vor der varAnweisung.

Dies kann verwirrend sein. Schauen wir uns also Folgendes an:

display("foo in window? " + ('foo' in window)); // displays "true"
display("window.foo = " + window.foo);          // displays "undefined"
display("bar in window? " + ('bar' in window)); // displays "false"
display("window.bar = " + window.bar);          // displays "undefined"
var foo = "f";
bar = "b";
display("foo in window? " + ('foo' in window)); // displays "true"
display("window.foo = " + window.foo);          // displays "f"
display("bar in window? " + ('bar' in window)); // displays "true"
display("window.bar = " + window.bar);          // displays "b"

Live-Beispiel:

Wie Sie sehen können, wird das Symbol foovor der ersten Zeile definiert, das Symbol barjedoch nicht. Wo sich die var foo = "f";Anweisung befindet, gibt es zwei Dinge: Definieren des Symbols, bevor die erste Codezeile ausgeführt wird; und Zuweisen zu diesem Symbol, was geschieht, wenn sich die Linie im schrittweisen Ablauf befindet. Dies wird als " varHeben" bezeichnet, da das var fooTeil an die Oberseite des Oszilloskops verschoben ("gehoben") wird, das foo = "f"Teil jedoch an seiner ursprünglichen Position belassen wird. (Siehe Schlechtes Missverständnisvar in meinem anämischen kleinen Blog.)


Wann letund constpassieren

letund constunterscheiden sich varin vielerlei Hinsicht. Die Art und Weise, auf die Frage relevant ist , ist , dass , obwohl die Bindung sie definieren , erstellt vor jedem Schritt- für -Schritt - Code ausgeführt wird , ist es nicht zugänglich , bis die letoder constAnweisung erreicht ist.

Also, während dies läuft:

display(a);    // undefined
var a = 0;
display(a);    // 0

Dies wirft einen Fehler aus:

display(a);    // ReferenceError: a is not defined
let a = 0;
display(a);

Die anderen beiden Arten, die sich von denen unterscheiden letund für die Frage nicht wirklich relevant sind, sind:constvar

  1. varimmer gilt für den gesamten Ausführungskontext ( im gesamten globalen Code oder im gesamten Funktionscode in der Funktion , wo es angezeigt wird ), aber letund constgelten nur innerhalb des Blockes , wo sie angezeigt werden . Das heißt, varhat die Funktion (oder global) Umfang, sondern letund constBlock Umfang hat.

  2. Das Wiederholen var aim selben Kontext ist harmlos, aber wenn Sie let a(oder const a) haben, ist es ein Syntaxfehler , ein anderes let aoder ein const aoder ein zu haben var a.

Hier ist ein Beispiel, das dies demonstriert letund constsofort in ihrem Block wirksam wird, bevor Code in diesem Block ausgeführt wird, auf den jedoch erst mit der Anweisung letoder constzugegriffen werden kann:

var a = 0;
console.log(a);
if (true)
{
  console.log(a); // ReferenceError: a is not defined
  let a = 1;
  console.log(a);
}

Beachten Sie, dass die zweite console.logfehlschlägt, anstatt avon außerhalb des Blocks auf die zuzugreifen .


Off-Topic: Vermeiden Sie Unordnung im globalen Objekt ( window)

Das windowObjekt wird sehr, sehr voll mit Eigenschaften. Wenn immer möglich, empfehlen wir dringend, das Chaos nicht zu vergrößern. Wickeln Sie stattdessen Ihre Symbole in ein kleines Paket und exportieren Sie höchstens ein Symbol in das windowObjekt. (Ich exportiere häufig keine Symbole in das windowObjekt.) Sie können eine Funktion verwenden, um Ihren gesamten Code zu enthalten, um Ihre Symbole zu enthalten, und diese Funktion kann anonym sein, wenn Sie möchten:

(function() {
    var a = 0; // `a` is NOT a property of `window` now

    function foo() {
        alert(a);   // Alerts "0", because `foo` can access `a`
    }
})();

In diesem Beispiel definieren wir eine Funktion und lassen sie sofort ausführen ( ()am Ende).

Eine auf diese Weise verwendete Funktion wird häufig als Scoping-Funktion bezeichnet . Funktionen definiert innerhalb der Scoping - Funktion kann den Zugriff Variablen in der Scoping - Funktion definiert , weil sie Schließungen über diese Daten (siehe: Verschlüsse sind nicht kompliziert auf meinem anämisch kleinen Blog).

TJ Crowder
quelle
Kann ich klarstellen window['a']=0, dass ich Fenster als Karte verwende? ist etwas windowBesonderes, so dass einige Browser dies nicht zulassen und mich zur Verwendung zwingen window.a?
Jayen
Ein Hinweis zu Nummer 3 , der wahrscheinlich klargestellt werden sollte: Funktioniert window.a = 0;nur in Browserumgebungen und nur gemäß Konvention. Das Binden des globalen Objekts an eine Variable mit dem Namen windowbefindet sich nicht in der ES-Spezifikation und funktioniert daher nicht in beispielsweise V8 oder Node.js, während this.a = 0;(wenn es im globalen Ausführungskontext aufgerufen wird) in jeder Umgebung funktioniert , da die Spezifikation dies angibt dass es ein globales Objekt geben muss . Wenn Sie Ihren Code wie im Abschnitt Off-Topic in ein IIFE einbinden , können Sie ihn thisals Parameter mit dem Namen windowoder globalals direkten Verweis auf das globale Objekt übergeben.
Sherlock_HJ
@Sherlock_HJ: Ich habe "in Browsern" hinzugefügt. Das ist auch früher in der Antwort, aber ich habe es hinzugefügt, falls die Leute darauf zurückgreifen. Es ist jetzt in der Spezifikation ; Während es nur im Vorbeigehen ist, werden Sie keinen Browser finden, der dies nicht tut. Ich bin etwas überrascht, dass es nicht in Anhang B steht .
TJ Crowder
@TJCrowder, Eine mit deklarierte globale Variable var a = 0;wird also automatisch zu einer Eigenschaft des globalen Objekts. Wenn ich var b = 0;innerhalb einer Funktionsdeklaration deklariere , ist dies auch eine Eigenschaft eines zugrunde liegenden Objekts?
Ezpresso
@ezpresso: Nein und ja. Sie werden zwar zu Eigenschaften eines Objekts (dem EnvironmentRecord der Variablenumgebung des ExecutionContext, in dem sie angezeigt werden; Details hier und hier ), aber es gibt keine Möglichkeit, über Programmcode direkt auf dieses Objekt zuzugreifen.
TJ Crowder
40

Einfach halten:

a = 0

Der obige Code gibt eine globale Bereichsvariable an

var a = 0;

Dieser Code gibt eine Variable an, die im aktuellen Bereich und darunter verwendet werden soll

window.a = 0;

Dies entspricht im Allgemeinen der globalen Variablen.

Umair Jabbar
quelle
Ihre Aussagen "Der obige Code gibt eine globale Bereichsvariable an" und "Dieser Code gibt eine Variable an, die im aktuellen Bereich und darunter verwendet werden soll" lassen darauf schließen, dass Sie die erste Zeile und den Zugriff a unter dem nicht verwenden können aktueller Geltungsbereich. Sie können. Außerdem ist Ihre Verwendung von "globaler Variable" etwas abwegig - die beiden Stellen, an denen Sie "globale Variable" sagen, sind nicht globaler als die Stelle, an der Sie es nicht sagen.
TJ Crowder
global selbst bedeutet, dass Sie überall auf die Variable zugreifen / sie lesen / schreiben können, einschließlich der Stelle, an der ich den aktuellen Bereich erwähnt habe, was so offensichtlich ist. Und wenn Sie vorschlagen, dass window.a und 'a' im Skript nicht global sind, liegen Sie zu 100% falsch.
Umair Jabbar
3
@Umair: "global selbst bedeutet, dass Sie überall auf die Variable zugreifen / sie lesen / schreiben können" Richtig. Wieder scheinen Sie das Erste und das Letzte als "globaler" als das Mittlere zu bezeichnen, was sie natürlich nicht sind.
TJ Crowder
4
Die mittlere Funktion wird als innerhalb einer Funktion verwendet betrachtet. Alle Funktionen sind gleich, wenn sie im Hauptbereich verwendet werden. Die Verwendung von var in einer Funktion war meine Annahme
Umair Jabbar
4
@Umair: "Die Verwendung von var in einer Funktion war meine Annahme" Ah, okay. Aber das ist nicht die Frage. Die Frage lautet sehr deutlich "im globalen Geltungsbereich" . Wenn Sie die Annahme ändern möchten (was fair genug ist, um einen allgemeineren Punkt zu erweitern und zu erklären), müssen Sie klar sein, dass Sie dies in Ihrer Antwort tun.
TJ Crowder
10
<title>Index.html</title>
<script>
    var varDeclaration = true;
    noVarDeclaration = true;
    window.hungOnWindow = true;
    document.hungOnDocument = true;
</script>
<script src="external.js"></script>

/* external.js */

console.info(varDeclaration == true); // could be .log, alert etc
// returns false in IE8

console.info(noVarDeclaration == true); // could be .log, alert etc
// returns false in IE8

console.info(window.hungOnWindow == true); // could be .log, alert etc
// returns true in IE8

console.info(document.hungOnDocument == true); // could be .log, alert etc
// returns ??? in IE8 (untested!)  *I personally find this more clugy than hanging off window obj

Gibt es ein globales Objekt, an dem standardmäßig alle Variablen hängen? zB: 'globals.noVar-Deklaration'

Cody
quelle
Sehr schöne Erkundung. Die definitive Anleitung zur Verwendung der window.*Deklaration. Diese Deklaration sieht am sichersten gegen das Kopieren und Einfügen Ihres Codes aus und ist auch klar.
Dan
7

Gestützt auf die exzellente Antwort von TJ Crowder : ( Off-Topic: Vermeiden Sie Unordnungwindow )

Dies ist ein Beispiel für seine Idee:

Html

<!DOCTYPE html>
<html>
  <head>
    <script type="text/javascript" src="init.js"></script>
    <script type="text/javascript">
      MYLIBRARY.init(["firstValue", 2, "thirdValue"]);
    </script>
    <script src="script.js"></script>
  </head>

  <body>
    <h1>Hello !</h1>
  </body>    
</html>

init.js (Basierend auf dieser Antwort )

var MYLIBRARY = MYLIBRARY || (function(){
    var _args = {}; // private

    return {
        init : function(Args) {
            _args = Args;
            // some other initialising
        },
        helloWorld : function(i) {
            return _args[i];
        }
    };
}());

script.js

// Here you can use the values defined in the html as if it were a global variable
var a = "Hello World " + MYLIBRARY.helloWorld(2);

alert(a);

Hier ist die plnkr . Hoffe es hilft!

robe007
quelle
5

Im globalen Bereich gibt es keinen semantischen Unterschied.

Sie sollten dies jedoch unbedingt vermeiden, a=0da Sie einen Wert für eine nicht deklarierte Variable festlegen.

Verwenden Sie auch Verschlüsse, um die Bearbeitung des globalen Bereichs überhaupt zu vermeiden

(function() {
   // do stuff locally

   // Hoist something to global scope
   window.someGlobal = someLocal
}());

Verwenden Sie immer Verschlüsse und heben Sie immer den globalen Geltungsbereich an, wenn dies unbedingt erforderlich ist. Sie sollten ohnehin für den größten Teil Ihrer Kommunikation die asynchrone Ereignisbehandlung verwenden.

Wie @AvianMoncellor erwähnt hat, gibt es einen IE-Fehler, bei dem var a = foonur ein globaler Dateibereich deklariert wird. Dies ist ein Problem mit dem berüchtigten kaputten Interpreter des IE. Dieser Fehler kommt mir bekannt vor, daher ist er wahrscheinlich wahr.

Also bleib bei window.globalName = someLocalpointer

Raynos
quelle
2
"Im globalen Bereich gibt es keinen semantischen Unterschied." Tatsächlich gibt es einen großen semantischen Unterschied, die Mechanismen, durch die die Eigenschaft definiert wird, sind völlig unterschiedlich - aber in der Praxis läuft es nur auf einen kleinen tatsächlichen Unterschied hinaus (insofern kann man nicht deletea var).
TJ Crowder
@TJ Crowder Das wusste ich nicht. Ich dachte, die Variablendeklaration würde Eigenschaften für das Variablenobjekt festlegen. Wusste nicht, dass diese nicht gelöscht werden konnten.
Raynos
Jep. Sie werden auch früher definiert, wenn Sie verwenden var. Es sind nur völlig unterschiedliche Mechanismen, die fast das gleiche praktische Ergebnis haben. :-)
TJ Crowder
@TJ Crowder Ich habe vergessen zu erwähnen, dass vardas zum Stillstand kommt.
Raynos