JavaScript catch子句范围

Jam*_*ice 31 javascript

ECMAScript的5规范规定如下:

通常,词汇环境与ECMAScript代码的某些特定语法结构相关联,例如TryStatement的FunctionDeclaration,WithStatement或Catch子句,并且每次评估此类代码时都会创建新的词法环境.

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

function example() {
    var x = 10;
    console.log(x); //10
}
console.log(x); //ReferenceError
Run Code Online (Sandbox Code Playgroud)

因此,在上面的函数声明中,创建了一个新的词法环境,这意味着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?
Run Code Online (Sandbox Code Playgroud)

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

Tha*_*hai 31

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

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

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

与WithStatement一样,with ({x: 1, y: 2}) { }x和y仅存在于with块内.

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

10.5声明绑定实例化:当输入执行上下文时,它将查找函数声明,参数和变量声明,然后在执行上下文的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
Run Code Online (Sandbox Code Playgroud)

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

  • 请注意,即使在包含try-catch的函数顶部声明`e`,`catch`块中的`e`也不会是同一个变量.与函数参数一样,它是词法上下文本地的新变量. (4认同)

Koo*_*Inc 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
Run Code Online (Sandbox Code Playgroud)

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

  1. oldEnv成为正在运行的执行上下文的LexicalEnvironment.
  2. catchEnv成为调用NewDeclarativeEnvironment传递oldEnv作为参数的结果.

同样在该部分的最后,它指出:

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


Chr*_*ois 6

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

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

ES2015:

{ let priv = 1; }
console.log(priv); // throws ReferenceError
Run Code Online (Sandbox Code Playgroud)

对此:

ES5 及以下:

try { throw void 0 } catch (priv) { priv = 1 }
console.log(priv); // throws ReferenceError
Run Code Online (Sandbox Code Playgroud)