2009-12-19 12 views
6

Ho incontrato un problema molto strano (per me) con la parola chiave var. L'ho ridotto a un caso di test abbastanza minimale, e ho scoperto che è esposto in Node.js (quindi, V8 e Chrome), Inspector di Safari 4 (quindi, Nitro) e FireBug (ovviamente, SpiderMonkey). Inizialmente stavo preparando un bug report, ma dal momento che è così ampiamente visualizzato, assumerò che io fraintenda completamente il modo in cui JavaScript suppone l'ambito e la ricerca delle variabili.Operazione confusa della parola chiave `var` di JavaScript

Il banco di prova è molto piccolo, e su GitHub qui: http://gist.github.com/260067. L'unica differenza tra il primo e il secondo esempio è l'inclusione della parola chiave var.

Qui, pure, è un banco di prova simile che presenta lo stesso 'problema' in un modo diverso: https://gist.github.com/698b977ee0de2f0ee54a

Edit: da escludere qualsiasi altre risposte che cercano di spiegare come funziona portata a cascata, io' Mi sono intimamente familiare. Il mio problema è che io non capisco perché il seguente codice 'funziona' (in quanto alert() s 'esterno', seguito da 'interna', e poi di nuovo 'esterna'):

(function(){ 
    var foo = 'outer'; 
    alert("Outer `foo`: " + foo); 

    (function(){ 
    foo = 'inner'; 
    alert("Inner `foo`: " + foo); 

    var foo; 
    })(); 

    alert("Outer `foo`: " + foo); 
})(); 

Il var foo; si verifica in una posizione del tutto irrilevante alla riassegnazione di foo; quindi perché influenza questo incarico in modo sostanziale?

+0

Perché stai utilizzando una valutazione? Non c'è assolutamente alcun motivo per usare eval nel codice che hai postato. – Marius

+0

Per dimostrare il problema. L'effettiva implementazione è molto diversa; puoi vederlo allo stato naturale qui: http://github.com/elliottcable/poopy.js/blob/new-acquire/lib/from.js#L193 – ELLIOTTCABLE

+0

Il tuo ultimo esempio funziona perché 1) foo = 'inner'; assegna il valore allo scope genitore foo, a cui questa funzione ha accesso. 2) non è necessario utilizzare var per dichiarare una variabile. – Marius

risposta

14

Il fatto è che a differenza di altri linguaggi, JavaScript crea tutte le variabili all'inizio di una funzione. Ciò significa che il codice:

(function(){ 
    if(myVar == undefined){ 
     alert(myVar); 
    } 
    if(myVar == undefined){ 
     var myVar = 5; 
    } 
})(); 

è effettivamente compilato e interpretato come

(function(){ 
    var myVar; 
    if(myVar == undefined){ 
     alert(myVar); 
    } 
    if(myVar == undefined){ 
     myVar = 5; 
    } 
})(); 

Per creare una variabile e hanno solo a disposizione all'interno di un caso o di ciclo di blocco, è necessario utilizzare let, che è una nuova funzione JavaScript. Non sono sicuro di quanti browser lo implementino ancora (Firefox 3.5 fa se usi <script type="text/javascript;version=1.7">).

(function(){ 
    if(myVar == undefined){ 
     alert(myVar); 
    } 
    if(myVar == undefined){ 
     let myVar = 5; 
    } 
})(); 
+0

Sì, ne sono a conoscenza, vedere la mia risposta ad Amnon sopra. La mia confusione deriva dal fatto che influisce sulle chiamate da * prima che sia definito nell'ambito locale *. – ELLIOTTCABLE

+0

Sì, ho finalmente capito la tua domanda e ho riscritto la mia risposta. Spero che questo ti aiuti. – Marius

+0

Sì, questo ha aiutato un bel po '. Vedi il mio chiarimento nella domanda originale. Non riesco ancora a capire perché questo accada come fa; Suppongo che la mia comprensione intuitiva di JavaScript, il linguaggio, non sia in linea con le specifiche/l'implementazione. Indipendentemente da ciò, ho intenzione di marcare questa risposta come/= – ELLIOTTCABLE

1

l'inclusione di var significa che l'assegnazione del {} è fatto per un locale esportazioni variabile invece delle esportazioni delle variabili globali, il che significa che non ha alcun effetto.

+0

Sì. Che capisco Io * faccio * sapere JavaScript di base. Il mio problema è che viene * dopo * la chiamata che stiamo esaminando; non dovrebbe avere alcun effetto sul fatto che 'exports' non sia definito al momento della chiamata' alert() '. Indipendentemente da ciò che viene dopo, 'exports' è * non * non definito a quel punto. – ELLIOTTCABLE

2

var exports non funziona esattamente come le variabili locali in molte lingue. Dichiara exports come variabile locale nell'intera funzione anziché solo il blocco che lo contiene (anche se appare dopo il primo utilizzo), quindi l'argomento della funzione con lo stesso nome è nascosto.

Modifica: la parola chiave let funziona in modo più convenzionale (dichiara una variabile solo per il blocco contenitore) ma non è disponibile in tutte le versioni di JavaScript.

+0

Quindi l'interprete legge in anticipo e nota la dichiarazione della variabile ... e la definizione della variabile nello scope locals (associata a 'undefined'), sovrascrive quella dell'argomento? Questo è * estremamente * contro-intuitivo. Come mai l'interprete legge avanti in questo caso? ò.ò – ELLIOTTCABLE

+1

Sì, è contro-intuitivo, ma è così che è ... Per quanto riguarda come è fatto, la funzione viene analizzato prima che venga eseguito. – Amnon

+0

"" "Come mai l'interprete legge avanti in questa istanza?" "" - guarda avanti in OGNI istanza. Non è un parse/interprete "riga per riga" come alcuni altri linguaggi, il parser legge TUTTI gli script prima che inizi a eseguire qualsiasi cosa. – Hejazzman

Problemi correlati