2011-10-28 17 views
30

Los ECMAScript 5 spec indica lo siguiente:JavaScript alcance cláusula catch

lo general, un entorno léxico se asocia con algunos estructura sintáctica específica de código ECMAScript tal como un FunctionDeclaration, un WithStatement, o una cláusula de captura de un TryStatement y un nuevo léxico Medio Ambiente se crea cada vez que se evalúa dicho código.

Si mi interpretación es correcta, entonces cuando se crea un nuevo entorno léxico en JavaScript, se introduce un nuevo ámbito, por lo que las variables declaradas dentro de una función no son visibles fuera de esa función:

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

Así que en la declaración de la función anterior, se crea un nuevo entorno léxico, lo que significa x no está disponible en todos los ambientes exteriores léxicas que puedan existir.

Así que la parte de la cita anterior sobre las declaraciones de funciones parece tener sentido. Sin embargo, también afirma que se crea un nuevo entorno léxico de la cláusula de captura de una sentencia 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? 

Entonces, ¿cómo el alcance de una obra catch bloque? ¿Tengo un malentendido fundamental de lo que es un entorno léxico?

+0

Esta publicación de SO está relacionada de alguna manera: http://stackoverflow.com/questions/6100230/javascript-catch-parameter-already-defined – Juri

Respuesta

29

Si lo entiendo bien, entonces lo que probablemente significa es que, en su código,

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

e solo existirán en el bloque catch. Pruebe console.log(e); fuera del bloque catch y lanzará ReferenceError.

Al igual que con WithStatement, with ({x: 1, y: 2}) { }, xey solo existirán dentro del bloque with.

Pero no significa que las declaraciones var estarán vinculadas al entorno léxico más cercano. En realidad, las declaraciones var estarán vinculadas al entorno cuando se ingrese un contexto de ejecución.

10.5 Declaration Binding Instantiation: cuando se ingresa el contexto de ejecución, buscará declaraciones de funciones, argumentos y declaraciones de variables y luego creará enlaces en el entorno variable de contexto de ejecución.

Por lo tanto, cualquier variable que se declare usando var será accesible en cualquier lugar de la función independientemente de las estructuras de control o donde se define dentro de la función. Tenga en cuenta que esto no incluye las funciones anidadas, ya que son un contexto de ejecución independiente.

Eso significa que las declaraciones var estarán vinculadas al contexto de ejecución más cercano.

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

Así que en el código de seguridad, x = 5; establecerá la variable x dentro de la función interior, porque var x; dentro if (false) { var x; } va ligada a la función ya antes de que se ejecute el código de función.

+1

Ahh, esto tiene mucho sentido. El "Por lo tanto, cualquier variable que se declara utilizando el párrafo 'var' ..." hace que haga clic. Gracias :) –

+0

Gran respuesta y bien explicada – contactmatt

+4

Tenga en cuenta que incluso si declara 'e' en la parte superior de la función que contiene el try-catch, la' e' en el bloque 'catch' no será la misma variable. Al igual que los parámetros de función, es una nueva variable local para el contexto léxico. –

8

De dev.opera (énfasis añadido)

El try-catch-finally construcción es bastante único. A diferencia de otras construcciones, crea una nueva variable en el ámbito actual en tiempo de ejecución. Esto ocurre cada vez que se ejecuta la cláusula catch, donde el objeto de excepción capturado se asigna a una variable. Esta variable no existe dentro de otras partes del script, incluso dentro del mismo ámbito. Se crea al comienzo de la cláusula catch y luego se destruye al final.

Parece que lo único que está realmente dentro del alcance de la captura es la excepción en sí misma. Otras acciones parecen estar (o permanecer) vinculadas al alcance externo de la captura (por lo tanto, en el ejemplo, el alcance global).

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

En ES5 estas líneas pueden ser relevantes (negrita/cursiva es nuestra) a esto:

  1. Vamos oldEnv ser LexicalEnvironment del contexto de ejecución correr.
  2. Vamos catchEnv ser el resultado de llamar NewDeclarativeEnvironment pasar oldEnv como argumento.

También al final de esta parte se afirma:

NOTA: No importa cómo deja el control del Bloque la LexicalEnvironment se restablece a su estado anterior

+0

Sí, he notado que el objeto de error estaba solo en el alcance dentro del 'catch'. Aún así no tiene sentido. Solo puedo suponer que, o todavía tengo un completo malentendido o (probablemente no es sorprendente) que la especificación no se haya seguido correctamente. Al leer la sección 12.14 (http://es5.github.com/#x12.14), parece decir que cualquier variable declarada dentro de 'catch' no estará dentro del alcance –

+0

Agregué 2 líneas de esa sección ES5 titulada "The Production Catch ...". Por lo que entiendo de esas líneas, solo el error se localiza localmente usando 'catchEnv' (6-8). Otras cosas vuelven a 'oldEnv' al final. – KooiInc

0

Estaba dando vueltas con esta pregunta, ya que parecía que ya había encontrado un problema similar, no relacionado con el bloque de captura en específico, pero dentro de una definición de función. Ahora acabo de recordar y en realidad también blogged about that problem hace un par de semanas :).

Tome este código:

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

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

Código: http://jsbin.com/alimuv/2/edit#javascript,live

¿Cuál es la salida ?? Al mirarlo rápidamente, uno podría asumir que es "Hola", ya que el alcance de la variable x se toma del alcance externo de la función. En su lugar, se imprime undefined. La razón es la misma que para la cuestión del bloque catch: alcance léxico, lo que significa que el alcance se crea en definición de función y no en tiempo de ejecución.

+1

Esto no habla de try/catch en absoluto. – Flimm

1

Dado que estaba estandarizado en ES3, una cláusula catch() {} es (que yo sepa) la única construcción javascript anterior a ES2015 que crea un ámbito de nivel de bloque, simplemente porque es la única construcción a nivel de bloque que viene con argumento.

Es la razón por la que ha sido utilizado como un polyfill de próxima generación Javascript transpilers para compilar esto:

ES2015:

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

a esto:

ES5 y inferior:

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