这个提升如何与块作用域一起工作?
我被问到一个问题
{
function foo() {
console.log('A');
}
foo();
foo = 1;
function foo() {
console.log('B');
}
foo = 2;
console.log(foo);
}
console.log(foo);
回答
根据函数声明处的web compat 语义,阻塞作用域变量的值绑定到外部作用域²。此代码等效于:
let outerFoo; // the functions create a binding outside of the scope
{
let innerFoo; // but also inside
// due to hoisting, functions get bound before any code get's executed:
innerFoo = function foo() {
console.log('A');
};
innerFoo = function foo() {
console.log('B');
};
// At the place of the function declaration, the variable leaves the scope
/* function foo() {
console.log('A');
} */
outerFoo = innerFoo;
innerFoo();
innerFoo = 1;
// this also applies to the second declaration
/* function foo() {
console.log('B');
} */
outerFoo = innerFoo;
innerFoo = 2;
console.log(innerFoo);
}
console.log(outerFoo);
²这基本上就是规范描述它的方式:
When the FunctionDeclaration f is evaluated, perform the following steps in place of the FunctionDeclaration Evaluation algorithm provided in 15.2.6: a. Let fenv be the running execution context's VariableEnvironment. b. Let benv be the running execution context's LexicalEnvironment. c. Let fobj be ! benv.GetBindingValue(F, false). d. Perform ! fenv.SetMutableBinding(F, fobj, false).
该规范还指出:
在 ECMAScript 2015 之前,ECMAScript 规范没有将 FunctionDeclaration 的出现定义为 Block 语句的 StatementList 的元素。但是,对这种形式的 FunctionDeclaration 的支持是一种允许的扩展,并且大多数浏览器托管的 ECMAScript 实现都允许它们。不幸的是,这些声明的语义在这些实现中有所不同。由于这些语义差异,使用块级函数声明的现有 Web ECMAScript 代码仅在浏览器实现之间可移植,前提是使用仅取决于此类声明的所有浏览器实现的语义交集
所以 Safari 可能会像往常一样做这件事,而 Chrome(和 Firefox)则遵循规范。
- @BenjaminGruenbaum yeah, WebCompat semantics are my favourite part of the specification, because the behavior seems so random if you don't know how it is specified 🙂 Already answered a few of these questions so I have this one basically bookmarked