JavaScript catch子句作用域

JavaScript catch clause scope

本文关键字:作用域 子句 catch JavaScript      更新时间:2023-09-26

ECMAScript 5规范声明如下:

通常词法环境与一些特定的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块的作用域是如何工作的?我对什么是词法环境有根本性的误解吗?

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

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声明绑定实例化:当进入执行上下文时,它会查找函数声明、参数和变量声明,然后在执行上下文的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变量,因为if (false) { var x; }中的var x;在函数代码执行之前已经绑定到该函数。

From dev.opera

try-catch-finally构造是相当独特的。与其他构造不同,在运行时在当前作用域中创建一个新变量。每次执行catch子句时都会发生这种情况,其中捕获的异常对象被分配给一个变量。该变量不存在于脚本的其他部分中,即使在同一作用域中也是如此。它在catch子句开始时创建,然后在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. catchEnv是调用NewDeclarativeEnvironment传递的结果

在那部分的末尾也写着:

注意:无论控件如何离开Block LexicalEnvironment总是恢复到原来的状态

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

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

ES2015:

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

:

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

我对这个问题感到困惑,因为我似乎已经遇到了类似的问题,与特定的catch块无关,而是在函数定义中。现在我才想起来,实际上我在几周前也写过关于这个问题的博客:)。

用下面的代码:

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

代码:http://jsbin.com/alimuv/2/edit javascript,生活

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