在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; }执行函数代码之前内部已经绑定到该函数.
来自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中,这些行可能与此相关(粗体/强调添加):
- 让oldEnv成为正在运行的执行上下文的LexicalEnvironment.
- 让catchEnv成为调用NewDeclarativeEnvironment传递oldEnv作为参数的结果.
同样在该部分的最后,它指出:
注意:无论控制如何离开Block ,LexicalEnvironment总是恢复到以前的状态
由于它在 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)