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

我被问到一个问题

{
  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

以上是这个提升如何与块作用域一起工作?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>