为什么catch子句有自己的词汇环境?

bfa*_*tto 5 javascript scope try-catch ecmascript-5 hoisting

请考虑ECMA-262 v5.1的以下摘录(我最近在这个问题中看到过):

词汇环境是一种规范类型,用于根据ECMAScript代码的词法嵌套结构定义标识符与特定变量和函数的关联.词汇环境由环境记录和外部词汇环境的可能空引用组成.通常,词汇环境与ECMAScript代码的某些特定语法结构相关联,例如TryStatement的FunctionDeclaration,WithStatement或Catch子句,并且每次评估此类代码时都会创建新的词法环境.

我认为这意味着catch子句的主体会像函数那样提升自己的变量,但显然情况并非如此:

var a = 1;
try {
    console.log(x); // ReferenceError
} catch(ex) {
    console.log(a); // 1, not undefined
    var a = 3;
}
Run Code Online (Sandbox Code Playgroud)

有人知道为什么吗?另外,为什么一个catch条款需要自己的词汇环境?

Ber*_*rgi 6

是的,catch条款确实有自己的词汇环境.查看评估时会发生什么:它创建一个新的(从当前的一个派生)并将异常标识符绑定到它.执行catch块时,当前执行上下文 LexicalEnvironment切换到新的,而VariableEnvironment(" 其环境记录包含由VariableStatements和创建的绑定FunctionDeclarations)保持不变.

console.log(a); // undefined - declared from within the catch,
                // but in the current VariableEnvironment
a = 1;
console.log(typeof ex); // undefined - no binding
try {
    console.log(ex); // a ReferenceError in this LexicalEnvironment
} catch (ex) { // introducing the new LexicalEnvironment
    console.log(ex); // …and it works here!
    var a = 3; // variable declaration
}
Run Code Online (Sandbox Code Playgroud)

有趣的事实:如果你试图在一个子句中声明一个函数catch(虽然在块中语法无效,经常接受"函数声明语句"),它的作用域将成为当前的,VariableEnvironment因此它将无法访问异常:

try {throw "some"} catch(x) { function y(){console.log(x, typeof x);} y(); }
                    // throws a ReferenceError for x   ^
Run Code Online (Sandbox Code Playgroud)

(更新:在ES6中不再是这样,其中块级函数声明有效并且在块范围内关闭)

  • @D_S_X 啊,我猜这将是另一种可能性,iirc Python 就是这样做的。但是,虽然我不知道设计者的选择,但对我来说似乎是合理的:标识符应该仅在它具有值的块中可用。是的,在 ES6 中,每个块都会创建一个新的词法环境。 (2认同)