JavaScript catch子句范围

时间:2011-10-28 07:18:05

标签: javascript

ECMAScript 5 spec声明如下:

  

通常,词汇环境与某些特定的相关联   ECMAScript代码的句法结构,如FunctionDeclaration,   一个WithStatement,一个TryStatement的Catch子句和一个新的Lexical   每次评估此类代码时都会创建环境。

如果我的理解是正确的,那么当在JavaScript中创建新的词法环境时,会输入一个新的范围,这就是为什么在函数内声明的变量在该函数之外是不可见的:

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

因此,在上面的函数声明中,创建了一个新的词法环境,这意味着x在任何可能存在的外部词汇环境中都不可用。

因此上面关于函数声明的引用部分似乎有意义。但是,它还声明为Try语句的Catch子句创建了一个新的词法环境:

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?

那么catch块的范围如何工作?我对词汇环境有什么根本的误解吗?

4 个答案:

答案 0 :(得分:30)

如果我理解正确,那么在您的代码中,它可能意味着什么

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

e仅存在于catch块中。在catch块之外尝试console.log(e);,它将抛出ReferenceError。

与WithStatement with ({x: 1, y: 2}) { }一样,x和y只存在于with块中。

但这并不意味着var声明将被绑定到最接近的词汇环境。实际上,var声明在进入执行上下文时将绑定到环境。

10.5 Declaration Binding Instantiation:当输入执行上下文时,它将查找函数声明,参数和变量声明,然后在执行上下文的VariableEnvironment中创建绑定。

因此,无论控制结构或函数内部定义的位置如何,使用var声明的任何变量都可以在函数中的任何位置访问。请注意,这不包括嵌套函数,因为它们是单独的执行上下文。

这意味着var声明将绑定到最近的执行上下文。

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

所以在上面的代码中,x = 5;会在内部函数中设置x变量,因为var x;里面的if (false) { var x; }在函数代码之前已经绑定到该函数执行。

答案 1 :(得分:9)

来自dev.opera(强调添加)

  

try-catch-finally结构非常独特。与其他构造不同,它在运行时在当前作用域中创建一个新变量。每次执行catch子句时都会发生这种情况,其中捕获的异常对象被分配给变量。即使在同一范围内,此变量也不存在于脚本的其他部分内。它在catch子句的开头创建,然后在它结束时销毁。

所以看起来真正属于捕获范围的唯一事情就是异常本身。其他操作似乎(或保持)绑定到catch的外部范围(因此在示例中为全局范围)。

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

ES5中,这些行可能与此相关(粗体/强调添加):

  
      
  1. oldEnv 成为正在运行的执行上下文的LexicalEnvironment
  2.   
  3. catchEnv 成为调用NewDeclarativeEnvironment传递的结果   oldEnv作为论据。
  4.   

同样在该部分的末尾,它指出:

  

注意:无论控制如何离开Block ,LexicalEnvironment都是   总是恢复到以前的状态

答案 2 :(得分:2)

由于它在ES3中被标准化,catch () {}子句(据我所知)是唯一一个创建块级范围的ES2015之前的javascript构造,因为它是唯一的块级构造< em>带有参数。

这就是为什么它被下一代javascript转换器用作polyfill来编译它的原因:

<强> ES2015:

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

到此:

ES5及更低版本

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

答案 3 :(得分:0)

我对这个问题感到困惑,因为我似乎已经遇到过类似的问题,与特定的catch块无关,但在函数定义中。现在我记得,我几个星期前也blogged about that problem:)。

拿这段代码:

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

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

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

输出是什么?通过快速查看它,人们可能会认为它是“Hi”,因为变量x的范围取自函数的外部范围。而是打印出undefined。原因与catch块问题相同:词法范围,这意味着范围是在函数定义而不是在运行时创建的。