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
块的范围如何工作?我对词汇环境有什么根本的误解吗?
答案 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中,这些行可能与此相关(粗体/强调添加):
- 让 oldEnv 成为正在运行的执行上下文的LexicalEnvironment 。
- 让 catchEnv 成为调用NewDeclarativeEnvironment传递的结果 oldEnv作为论据。
醇>
同样在该部分的末尾,它指出:
注意:无论控制如何离开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块问题相同:词法范围,这意味着范围是在函数定义而不是在运行时创建的。