2011-10-28 15 views
30

I ECMAScript 5 spec indica:JavaScript portata clausola catch

solito un ambiente lessicale è associato con alcuni specifici struttura sintattica di codice ECMAScript come un FunctionDeclaration, un WithStatement, o una clausola cattura di un TryStatement e un nuovo ambiente Lexical viene creato ogni volta che tale codice viene valutato.

Se la mia comprensione è corretta, poi, quando un nuovo ambiente lessicale viene creato in JavaScript, viene inserito un nuovo ambito, che è il motivo per cui le variabili dichiarate all'interno di una funzione non sono visibili al di fuori di quella funzione:

function example() { 
    var x = 10; 
    console.log(x); //10 
} 
console.log(x); //ReferenceError 

Pertanto, nella dichiarazione di funzione sopra descritta, viene creato un nuovo ambiente lessicale, il che significa che x non è disponibile in alcun ambiente Lexical esterno che possa esistere.

Quindi la parte della citazione sopra relativa alle dichiarazioni di funzione sembra avere un senso. Tuttavia, si afferma anche che un nuovo ambiente lessicale viene creato per la clausola catch di un'istruzione try:

try { 
    console.log(y); //ReferenceError so we enter catch 
} 
catch(e) { 
    var x = 10; 
    console.log(x); //10 
} 
console.log(x); //10 - but why is x in scope? 

Così come fa la portata di un'opera catch blocco? Ho un malinteso fondamentale su cosa sia un ambiente lessicale?

+0

Questo post SO è in qualche modo correlato: http://stackoverflow.com/questions/6100230/javascript-catch-parameter-already-defined – Juri

risposta

29

Se ho capito bene, allora quello che probabilmente significa è che, nel tuo codice,

try { 
    console.log(y); //ReferenceError so we enter catch 
} 
catch(e) { 
    var x = 10; 
    console.log(x); //10 
} 

e esiste solo nel blocco catch. Prova console.log(e); fuori dal blocco catch e genererà ReferenceError.

Come con WithStatement, with ({x: 1, y: 2}) { }, xey esiste solo all'interno del blocco con.

Ma ciò non significa che le dichiarazioni var saranno associate all'ambiente lessicale più vicino. In realtà, le dichiarazioni var saranno associate all'ambiente quando si immette un contesto di esecuzione.

10.5 Declaration Binding Instantiation: quando il contesto di esecuzione viene immesso, cercherà dichiarazioni di funzioni, argomenti e dichiarazioni di variabili e quindi crea associazioni nel VariableEnvironment del contesto di esecuzione.

Quindi qualsiasi variabile dichiarata utilizzando var sarà accessibile in qualsiasi punto della funzione indipendentemente dalle strutture di controllo o da dove è definita all'interno della funzione. Nota che questo non include le funzioni annidate in quanto sono un contesto di esecuzione separato.

Ciò significa che le dichiarazioni var saranno associate al contesto di esecuzione più vicino.

var x = 1; 
(function() { 
    x = 5; console.log(x); // 5 
    if (false) { var x; } 
    x = 9; console.log(x); // 9 
})(); 
console.log(x); // 1 

Quindi nel codice sopra, x = 5; imposterà la variabile x all'interno della funzione interna, perché var x; all'interno if (false) { var x; } è destinata a tale funzione già prima dell'esecuzione del codice funzione.

+1

Ah, questo ha molto senso. Il "Così qualsiasi variabile che viene dichiarata usando' var' ... "tipo di paragrafo fatto clic su. Grazie :) –

+0

Ottima risposta e ben spiegato – contactmatt

+4

Nota che anche se dichiari 'e' nella parte superiore della funzione contenente il try-catch, la' e' nel blocco 'catch' non sarà la stessa variabile. Come i parametri di funzione, è una nuova variabile locale al contesto lessicale. –

8

Da dev.opera (enfasi aggiunta)

Il try-catch-finally costrutto è abbastanza singolare. A differenza di altri costrutti, crea una nuova variabile nello scope corrente in fase di runtime. Ciò si verifica ogni volta che viene eseguita la clausola catch, in cui l'oggetto exception rilevato viene assegnato a una variabile. Questa variabile non esiste all'interno di altre parti dello script anche all'interno dello stesso ambito. Viene creato all'inizio della clausola catch, quindi distrutto alla fine di esso.

Quindi sembra che l'unica cosa che è veramente nell'ambito della cattura sia l'eccezione stessa. Altre azioni sembrano essere (o rimanere) vincolate all'ambito esterno della cattura (quindi nell'esempio l'ambito globale).

try { 
    console.log(y); //ReferenceError so we enter catch 
} 
catch(e) { 
    var x = 10; 
    console.log(x); //10 
} 
console.log(e.message); //=> reference error 

In ES5 queste righe potrebbero essere rilevanti (grassetto/corsivo è mio) a questo:

  1. Let oldEnv essere LexicalEnvironment del contesto di esecuzione in esecuzione.
  2. Let catchEnv essere il risultato della chiamata NewDeclarativeEnvironment passando oldEnv come argomento.

anche alla fine di quella parte si afferma:

NOTA: Non importa quanto il controllo lascia il blocco il LexicalEnvironment è sempre ripristinato al suo stato precedente

+0

Sì, ho notato che l'oggetto error era solo nell'ambito del 'catch'. Comunque non ha senso. Posso solo supporre che o abbia ancora un completo fraintendimento o (probabilmente non sorprendentemente) le specifiche non siano state seguite correttamente. Leggendo la sezione 12.14 (http://es5.github.com/#x12.14), sembra anche dire che qualsiasi variabile dichiarata all'interno di 'catch' non sarà inclusa al di fuori di essa –

+0

Ho aggiunto 2 righe da quella sezione ES5 intitolata "La cattura di produzione ...". Per quanto ne capisco da quelle righe, l'errore è localizzato a livello locale usando 'catchEnv' (6-8). Altre cose tornano a 'oldEnv' alla fine. – KooiInc

0

Mi stavo sconcertando con questa domanda perché mi sembrava di aver già incontrato un problema simile, non relativo al blocco catch in specifico ma all'interno di una definizione di funzione. Ora mi sono appena ricordato e in effetti anche io, blogged about that problem, un paio di settimane fa :).

Prendete questo codice:

function test(){ 
    var x = "Hi"; 

    (function(){ 
    console.log(x); 
    var x = "Hi, there!"; 
    })(); 
} 

Codice: http://jsbin.com/alimuv/2/edit#javascript,live

Qual è l'uscita ?? Guardandolo rapidamente, si potrebbe supporre che sia "Ciao", poiché l'ambito della variabile x è preso dall'ambito esterno della funzione. Invece, viene stampato undefined. Il motivo è lo stesso del problema del blocco catch: scope lessicale, ovvero l'ambito viene creato con la definizione di funzione e non in fase di esecuzione.

+1

Questo non parla affatto di try/catch. – Flimm

1

Da quando è stato standardizzato in ES3, una clausola catch() {} è (a mia conoscenza) l'unico pre-ES2015 javascript costrutto che crea un ambito a livello di blocco, semplicemente perché è l'unica a livello di blocco costrutto che dispongono di argomento.

E 'il motivo per cui è stato usato come un polyfill entro la prossima generazione javascript transpilers per compilare questo:

ES2015:

{ let priv = 1; } 
console.log(priv); // throws ReferenceError 

a questo:

ES5 e inferiore:

try { throw void 0 } catch (priv) { priv = 1 } 
console.log(priv); // throws ReferenceError