Differenza tra sintassi di dichiarazione delle variabili in Javascript (comprese le variabili globali)?


292

C'è qualche differenza tra dichiarare una variabile:

var a=0; //1

...per di qua:

a=0; //2

...o:

window.a=0; //3

nell'ambito globale?


2
AFAIK var a = 0; non funziona in IE quando si accede alla variabile tramite un altro file js esterno che è dichiarato in un altro file js
Aivan Monceller

Non so di window.a ma gli altri 2 modi sono gli stessi nell'ambito globale.
programmatore

1
@AivanMonceller davvero? link per favore.
Raynos,

@Raynos, lo provo sul mio sito web. IE6 per essere specifici. Non sono riuscito a far apparire il mio var enum che si trova su un file js esterno e lo sto riferendo come javascript inline su un file html
Aivan Monceller

@Ashwini Nell'ambito globale, window è l'oggetto globale (nei browser). var a = 1; console.log (a); console.log (win
leebriggs

Risposte:


557

Sì, ci sono un paio di differenze, anche se in termini pratici non sono generalmente grandi.

C'è un quarto modo, e a partire da ES2015 (ES6) ce ne sono altri due. Ho aggiunto il quarto modo alla fine, ma ho inserito i modi ES2015 dopo il n. 1 (vedrai il perché), quindi abbiamo:

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

Tali dichiarazioni hanno spiegato

1 # var a = 0;

Questo crea una variabile globale che è anche una proprietà dell'oggetto globale , a cui accediamo come windownei browser (o tramite thisun ambito globale, in codice non rigoroso). A differenza di altre proprietà, la proprietà non può essere rimossa tramite delete.

In termini di specifiche, crea un identificatore vincolante sul oggetto Ambiente Record per l' ambiente globale . Ciò lo rende una proprietà dell'oggetto globale perché l'oggetto globale è il punto in cui si tengono i bind identificativi per l'oggetto dell'ambiente globale. Questo è il motivo per cui la proprietà non è cancellabile: non è solo una proprietà semplice, è un'associazione identificativa.

L'associazione (variabile) viene definita prima dell'esecuzione della prima riga di codice (vedere "Quando varsuccede" di seguito).

Si noti che su IE8 e precedenti, la proprietà creata su windownon è enumerabile (non viene visualizzata nelle for..inistruzioni). In IE9, Chrome, Firefox e Opera, è enumerabile.


# 1.1 let a = 0;

Questo crea una variabile globale che non è una proprietà dell'oggetto globale. Questa è una novità di ES2015.

In termini di specifica, crea un identificativo vincolante sul Record di ambiente dichiarativo per l'ambiente globale anziché l' oggetto Record di ambiente. L'ambiente globale è l'unica ad avere una scissione Ambiente record, uno per tutti la roba vecchia che va avanti l'oggetto globale (l' oggetto Ambiente Record) e un altro per tutte le cose nuove ( let, const, e le funzioni create da class) che non lo fanno vai sull'oggetto globale.

L'associazione viene creata prima dell'esecuzione di qualsiasi codice passo-passo nel relativo blocco racchiuso (in questo caso, prima dell'esecuzione di qualsiasi codice globale), ma non è accessibile in alcun modo fino a quando l'esecuzione passo-passo non raggiunge l' letistruzione. Una volta che l'esecuzione raggiunge l' letistruzione, la variabile è accessibile. (Vedi "Quando lete constsuccede" di seguito.)


# 1.2 const a = 0;

Crea una costante globale, che non è una proprietà dell'oggetto globale.

constè esattamente come letse non fosse necessario fornire un inizializzatore (la = valueparte) e non è possibile modificare il valore della costante una volta creata. Sotto le copertine, è esattamente come letma con una bandiera sull'associazione dell'identificatore che dice che il suo valore non può essere modificato. L'utilizzo constfa tre cose per te:

  1. Lo rende un errore di analisi se si tenta di assegnare alla costante.
  2. Documenta la sua natura immutabile per altri programmatori.
  3. Consente al motore JavaScript di ottimizzare sulla base del fatto che non cambierà.

2 # a = 0;

Questo crea implicitamente una proprietà sull'oggetto globale . Poiché è una proprietà normale, puoi eliminarla. Consiglio di non farlo, potrebbe non essere chiaro a chiunque legga il tuo codice in seguito. Se si utilizza la modalità rigorosa di ES5, farlo (assegnando a una variabile inesistente) è un errore. È uno dei diversi motivi per utilizzare la modalità rigorosa.

E interessante, sempre su IE8 e precedenti, la proprietà creata non enumerabile (non appare nelle for..indichiarazioni). È strano, in particolare il numero 3 di seguito.


3 # window.a = 0;

Questo crea esplicitamente una proprietà sull'oggetto globale, usando il windowglobale che fa riferimento all'oggetto globale (sui browser; alcuni ambienti non browser hanno una variabile globale equivalente, come globalsu NodeJS). Poiché è una proprietà normale, puoi eliminarla.

Questa proprietà è enumerabile, su IE8 e precedenti, e su tutti gli altri browser che ho provato.


# 4 this.a = 0;

Esattamente come il n. 3, tranne per il fatto che stiamo facendo riferimento all'oggetto globale thisanziché al globale window. Questo non funzionerà in modalità rigorosa, tuttavia, perché in modalità globale in modalità rigorosa, thisnon ha un riferimento all'oggetto globale (ha undefinedinvece il valore ).


Eliminazione di proprietà

Cosa intendo per "eliminazione" o "rimozione" a? Esatto: rimozione della proprietà (interamente) tramite la deleteparola chiave:

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

deleterimuove completamente una proprietà da un oggetto. Non puoi farlo con le proprietà aggiunte windowindirettamente tramite var, l'oggetto deleteviene silenziosamente ignorato o genera un'eccezione (a seconda dell'implementazione JavaScript e se sei in modalità rigorosa).

Avvertenza : IE8 di nuovo (e presumibilmente prima, e IE9-IE11 in modalità "compatibilità" non funzionante): non ti consentirà di eliminare le proprietà windowdell'oggetto, anche quando ti dovrebbe essere permesso. Peggio ancora, genera un'eccezione quando provi ( prova questo esperimento in IE8 e in altri browser). Quindi quando si elimina windowdall'oggetto, è necessario essere difensivi:

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

Che tenta di eliminare la proprietà e, se viene generata un'eccezione, fa la cosa migliore successiva e imposta la proprietà su undefined.

Questo vale solo per l' windowoggetto e solo (per quanto ne so) per IE8 e precedenti (o IE9-IE11 in modalità "compatibilità" non funzionante). Altri browser vanno bene con l'eliminazione delle windowproprietà, soggetto alle regole sopra.


Quando varsuccede

Le variabili definite tramite la vardichiarazione vengono creati prima qualsiasi codice passo-passo nel contesto di esecuzione viene eseguito, e quindi la proprietà esiste ben prima della vardichiarazione.

Questo può essere fonte di confusione, quindi diamo un'occhiata:

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"

Esempio live:

Come puoi vedere, il simbolo fooè definito prima della prima riga, ma il simbolo barnon lo è. Dove si trova la var foo = "f";dichiarazione, ci sono davvero due cose: definire il simbolo, che accade prima che venga eseguita la prima riga di codice; e facendo un'assegnazione a quel simbolo, che accade dove la linea si trova nel flusso passo-passo. Questo è noto come " varsollevamento" perché la var fooparte viene spostata ("sollevata") nella parte superiore dell'ambito, ma la foo = "f"parte viene lasciata nella posizione originale. (Vedi Poor incompresovar sul mio anemico piccolo blog.)


Quando lete constsuccede

lete constsono diversi varin un paio di modi. Il modo rilevante per la domanda è che sebbene l'associazione che definiscono sia creata prima dell'esecuzione di qualsiasi codice passo-passo, non è accessibile fino a quando non viene raggiunta l' istruzione leto const.

Quindi, mentre questo funziona:

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

Questo genera un errore:

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

Gli altri due modi che differiscono lete che non sono realmente rilevanti per la domanda sono:constvar

  1. varapplica sempre all'intero contesto di esecuzione (in tutto codice globale, o tutto il codice funzione nella funzione in cui è visualizzato), ma lete constapplica solo nel blocco in cui compaiono. Cioè, varha funzione (o globale) portata, ma lete constavere la possibilità di blocco.

  2. Ripetersi var anello stesso contesto è innocuo, ma se si ha let a(o const a), avere un altro let ao a const ao a var aè un errore di sintassi.

Ecco un esempio che lo dimostra lete constdiventa immediatamente attivo nel loro blocco prima che venga eseguito qualsiasi codice all'interno di quel blocco, ma non è accessibile fino all'istruzione leto const:

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

Si noti che il secondo ha console.logesito negativo, invece di accedere a adall'esterno del blocco.


Fuori tema: evitare di ingombrare l'oggetto globale ( window)

L' windowoggetto diventa molto, molto ingombro di proprietà. Quando possibile, consigliamo vivamente di non aggiungere al pasticcio. Invece, avvolgi i tuoi simboli in un piccolo pacchetto ed esporta al massimo un simbolo windowsull'oggetto. (Spesso non esporto alcun simbolo windowsull'oggetto.) Puoi usare una funzione per contenere tutto il tuo codice al fine di contenere i tuoi simboli, e quella funzione può essere anonima se ti piace:

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

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

In questo esempio, definiamo una funzione e la eseguiamo immediatamente ( ()alla fine).

Una funzione utilizzata in questo modo viene spesso chiamata funzione di scoping . Le funzioni definite nella funzione scoping possono accedere alle variabili definite nella funzione scoping perché sono chiusure su quei dati (vedi: Le chiusure non sono complicate sul mio piccolo blog anemico).


posso fare window['a']=0per chiarire che sto usando Windows come una mappa? è windowspeciale tale che alcuni browser non lo consentono e mi costringono ad usare window.a?
Jayen,

Una nota su # 3 che probabilmente merita di essere chiarita: window.a = 0;funziona solo negli ambienti browser e solo per convenzione. Binding l'oggetto globale a una variabile denominata windownon è nel ES Spec e così non funziona, per esempio, V8 o Node.js, mentre this.a = 0;(se richiamato nel contesto di esecuzione globale) funzionerà in qualsiasi ambiente in quanto le specifiche non specifica che ci deve essere un oggetto globale. Se si racchiude il codice in un IIFE come nella sezione Off-topic , è possibile passare thiscome parametro denominato windowo globalper ottenere un riferimento diretto all'oggetto globale.
Sherlock_HJ,

@Sherlock_HJ: ho aggiunto "sui browser;" anche questo è precedente nella risposta, ma l'ho aggiunto nel caso in cui la gente salti a quello. Ora è nelle specifiche ; mentre è solo di passaggio, non troverai un browser che non lo fa. Sono un po 'sorpreso che non è in allegato B .
TJ Crowder,

@TJCrowder, Quindi, una variabile globale dichiarata con var a = 0;diventa automaticamente una proprietà dell'oggetto globale. Se dichiaro var b = 0;all'interno di una dichiarazione di funzione, sarà anche una proprietà di qualche oggetto sottostante?
ezpresso,

@ezpresso: No e sì. Essi diventano proprietà di un oggetto ( l'EnvironmentRecord del VariableEnvironment del ExecutionContext dove compaiono, dettagli qui e qui ), ma non c'è modo di accedere direttamente tale oggetto dal codice del programma.
TJ Crowder,

40

Semplificando:

a = 0

Il codice sopra fornisce una variabile di portata globale

var a = 0;

Questo codice fornirà una variabile da utilizzare nell'ambito corrente e sotto di esso

window.a = 0;

Questo è generalmente lo stesso della variabile globale.


Le sue dichiarazioni "Il codice dà sopra una variabile portata globale" e "Questo codice darà una variabile da utilizzare nell'ambito corrente, e sotto di essa" , nel loro insieme, suggeriscono che non è possibile utilizzare la prima linea e l'accesso a sotto il ambito attuale. Puoi. Inoltre, il tuo uso di "variabile globale" è un po 'fuori - i due posti che dici "variabile globale" non sono più globali di quelli in cui non lo dici.
TJ Crowder,

globale stesso significa che è possibile accedere / leggere / scrivere la variabile ovunque, incluso il luogo in cui ho citato l'ambito attuale, che è così ovvio. E se suggerisci che window.a e 'a' non saranno globali nello script, allora sbagli al 100%.
Umair Jabbar,

3
@Umair: "globale stesso significa che puoi accedere / leggere / scrivere la variabile ovunque" Giusto. Ancora una volta, sembra che tu chiami il primo e l'ultimo come più "globale" rispetto al mezzo, che ovviamente non lo sono.
TJ Crowder,

4
quello centrale è considerato usato all'interno di una funzione, tutti sarebbero uguali se usati nell'ambito principale. usare var all'interno di una funzione era il mio presupposto
Umair Jabbar,

4
@Umair: "usare var all'interno di una funzione era il mio presupposto" Ah, ok. Ma questa non è la domanda. La domanda dice molto chiaramente "nell'ambito globale" . Se cambierai il presupposto (che è abbastanza giusto, per espandere e spiegare un punto più generale), dovrai essere chiaro che è quello che stai facendo nella tua risposta.
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

Esiste un oggetto globale di cui tutti i vars sono sospesi di default? ad esempio: "dichiarazione globals.noVar"


Esplorazione molto bella. La guida definitiva all'uso della window.*dichiarazione. Questa dichiarazione sembra più sicura contro il copia-incolla del codice e chiara anche.
Dan,

7

Ottimizzato dall'eccellente risposta di TJ Crowder : ( Off-topic: evitare ingombriwindow )

Questo è un esempio della sua idea:

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 (Basato su questa risposta )

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);

Ecco il plnkr . Spero che sia d'aiuto!


5

Nell'ambito globale non vi è alcuna differenza semantica.

Ma dovresti davvero evitare, a=0poiché hai impostato un valore su una variabile non dichiarata.

Utilizzare anche le chiusure per evitare di modificare l'ambito globale

(function() {
   // do stuff locally

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

Usa sempre le chiusure e solleva sempre l'ambito globale quando è assolutamente necessario. Dovresti comunque utilizzare la gestione asincrona degli eventi per la maggior parte delle tue comunicazioni.

Come accennato da @AvianMoncellor, esiste un bug IE var a = fooche dichiara solo un ambito globale per il file. Questo è un problema con il noto interprete rotto di IE. Questo bug sembra familiare, quindi è probabilmente vero.

Quindi attenersi a window.globalName = someLocalpointer


2
"Nell'ambito globale non esiste alcuna differenza semantica." In realtà, c'è una grande differenza semantica, i meccanismi con cui la proprietà viene definita sono completamente diversi - ma in termini pratici si riduce a solo una piccola reale differenza (in quanto non è possibile deleteuna var).
TJ Crowder,

@TJ Crowder Non lo sapevo. Pensavo che la dichiarazione delle variabili stesse impostando le proprietà sull'oggetto variabile. Non sapevo che non potevano essere cancellati.
Raynos,

Sì. Sono anche definiti in precedenza se si utilizza var. Sono solo meccanismi completamente diversi che hanno praticamente lo stesso risultato pratico. :-)
TJ Crowder,

@TJ Crowder Ho dimenticato di menzionare che varsalta all'arresto del campo di applicazione.
Raynos,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.