【问题标题】:javascript function Hoisting inside block statementjavascript函数在块语句中提升
【发布时间】:2021-01-04 23:29:47
【问题描述】:

{function foo(){};foo=1;function foo(){};foo=2;}
console.log(foo); // 1

谁能解释这里为什么输出“1”?

编辑: 似乎有一个实现差异,在“Chrome”、“Firefox”、“Nodejs”中输出为“1”,但在“Safari”中输出为“2”

【问题讨论】:

  • 长话短说,如果你不想踩得很薄,“使用严格”;
  • 是的,它有点与这个问题有关。但是让我困惑的是为什么在第二个函数声明之后,对“foo”的进一步引用只指另一个块级变量“foo”而不是 window.foo

标签: javascript hoisting


【解决方案1】:

让我们美化块并分解它:

{
  function foo() {};
  foo = 1;

  function foo() {};
  foo = 2;
}
console.log(foo); // 1

块范围的函数声明通常将其变量名从其块中提升出来(值为undefined),但只在块内接收一次值。您可以查看有关该行为的更多详细信息here

但是这种情况有点不同——你在一个块中声明了一个函数两次。看起来第一个 foo 函数被分配给在块外可见的名称。如果您在代码中的不同位置将属性描述符记录在 window 上,您可以看到这一点。

看起来,一旦达到重复的函数声明,对块内foo 变量名的进一步引用将引用仅在块内的绑定。因此,底部的foo = 2,因为它在重复声明之后,所以在块内引用时只会导致2 值绑定到foo 名称。块外的值仍然是 foo 最后保存的值重复函数声明之前:

// Variable name is hoisted: it exists on the global object, but is undefined
console.log(Object.getOwnPropertyDescriptor(window, 'foo'));
{
  // Function declaration: global foo gets assigned to
  function foo() {};
  console.log(window.foo);
  // Assignment to foo name: global foo gets assigned to
  foo = 1;
  // Assignment to foo name: global foo gets assigned to
  foo = 3;
  console.log(window.foo);

  // Duplicate function declaration: past this point, foo now no longer refers to the global foo
  // but to a locally-scoped identifier
  function foo() {};
  // See how the window value remains at 3:
  console.log(window.foo);

  // So this assignemnt only changes the binding of the `foo` inside this block
  // while window.foo remains at 3:
  foo = 2;
}
console.log(foo); // 3

查看原始代码的另一种方式:

{
  function foo() {}; // this ALSO does: window.foo = function foo() {};
  foo = 1; // this ALSO does: window.foo = 1

  function foo() {};  // this actually does: fooLocal = function foo() {};
  // further references to "foo" in this block refer to fooLocal
  foo = 2; // this actually does: fooLocal = 2
}
console.log(foo); // references window.foo

【讨论】:

  • 我不确定这个解释是否有效,因为 JS 除了 let/const、IIUC 之外没有块范围。
  • 它不是正式通常设想的块作用域,它只是碰巧是在重复声明后导致看起来像块作用域的行为到达了。尽管没有使用constlet,但在Bergi's answer 中可以看到类似的块级行为。这似乎是发生了什么,但很可能是未定义的行为。
  • @BrianMcCutchon block scoped functions were introduced in ES6。为解析 JS 源代码的浏览器定义了 Web 兼容性语义,这些语义恰好有类似 OP 的代码,但它们与试图在同一块中混合多个函数声明的代码一样令人困惑和迟钝。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-02-19
  • 1970-01-01
  • 2016-05-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多