这个提升如何与块作用域一起工作?

Hao*_* Wu 26 javascript hoisting

我被问到一个问题

{
  function foo() {
    console.log('A');
  }
  
  foo();
  
  foo = 1;
  
  function foo() {
    console.log('B');
  }
  
  foo = 2;

  console.log(foo);
}
console.log(foo);
Run Code Online (Sandbox Code Playgroud)

为什么第三个输出是1而不是2


不应该foo创建块作用域,因为该块中既没有let也没有const。但是第二个foo输出2意味着确实foo已经创建了另一个引用。

到底是怎么回事?

PS 我正在使用 Chrome Version 89.0.4389.90 (Official Build) (x86_64)

Jon*_*lms 7


根据函数声明处的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);
Run Code Online (Sandbox Code Playgroud)

²这基本上就是规范描述它的方式:

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).
Run Code Online (Sandbox Code Playgroud)

该规范还指出:

在 ECMAScript 2015 之前,ECMAScript 规范没有将 FunctionDeclaration 的出现定义为 Block 语句的 StatementList 的元素。但是,对这种形式的 FunctionDeclaration 的支持是一种允许的扩展,并且大多数浏览器托管的 ECMAScript 实现都允许它们。不幸的是,这些声明的语义在这些实现中有所不同。由于这些语义差异,使用块级函数声明的现有 Web ECMAScript 代码仅在浏览器实现之间可移植,前提是使用仅取决于此类声明的所有浏览器实现的语义交集

所以 Safari 可能会像往常一样做这件事,而 Chrome(和 Firefox)则遵循规范。

  • @BenjaminGruenbaum 是的,WebCompat 语义是规范中我最喜欢的部分,因为如果您不知道它是如何指定的,则行为看起来很随机:) 已经回答了其中一些问题,所以我基本上已经为这个问题添加了书签 (2认同)