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?
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?
Risposte:
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
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:
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 ).
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.
varsuccedeLe 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.)
lete constsuccedelete 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
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.
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.
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).
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?
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.
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?
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.
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.
<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"
window.*dichiarazione. Questa dichiarazione sembra più sicura contro il copia-incolla del codice e chiara anche.
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!
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
deleteuna var).
var. Sono solo meccanismi completamente diversi che hanno praticamente lo stesso risultato pratico. :-)
varsalta all'arresto del campo di applicazione.