函数声明的求值顺序

mic*_*mor 5 javascript

对于以下代码,既不是 ES6,也不是“严格模式”,我预期结果为 'b',因为 的第二个声明foo应该覆盖第一个声明。但结果是“a”!

{
  function foo() {
    console.log('a');
  }
}

function foo() {
  console.log('b');
}

foo(); // 'a' ? Why not 'b'?
Run Code Online (Sandbox Code Playgroud)

当此代码被附加花括号括起来时,结果是预期的“b”。

{ // additional curly braces

  {
    function foo() {
      console.log('a');
    }
  }

  function foo() {
    console.log('b');
  }

  foo(); // 'b' as expected!

} // end additional curly braces 
Run Code Online (Sandbox Code Playgroud)

为了进一步说明,请考虑以下附加示例:

foo('before declaration'); // outcome:  from outside block :before declaration

{
  function foo(s) {
    console.log('from inside block: ' + s);
  }
}

function foo(s) {
  console.log('from outside block :' + s);
}

foo('after declaration'); // outcome:  from inside block: after declaration
Run Code Online (Sandbox Code Playgroud)

我认为正确的结果应该是:

// from outside block :before declaration
// from outside block :after declaration
Run Code Online (Sandbox Code Playgroud)

我无法在这里发现我的误解。

如果我再次将完整的最后一个示例括在大括号内,如下所示:

{
  foo('before declaration'); // outcome:  from outside block :before declaration

  {
    function foo(s) {
      console.log('from inside block: ' + s);
    }
  }

  function foo(s) {
    console.log('from outside block :' + s);
  }

  foo('after declaration'); // outcome:  from outside block: after declaration
}
Run Code Online (Sandbox Code Playgroud)

我得到了预期的结果。

650*_*502 -1

不支持在单个范围内对同一函数进行多个定义,除非它们位于顶层。

原因是两者之间存在很大差异,例如:

function square(x) { return x*x; }
Run Code Online (Sandbox Code Playgroud)

var square = function(x) { return x*x; }
Run Code Online (Sandbox Code Playgroud)

在第一种情况下,名称square在开始时绑定到函数对象,而不是在正常程序流期间“执行”定义时绑定到函数对象:

function foo() {
    console.log(square(12));  // Valid, no prob the function is defined "later"
    function square(x) { return x*x; }
}
Run Code Online (Sandbox Code Playgroud)

然而,这意味着如果您将不同的定义放在同一范围内,则不清楚应该做什么。

该标准描述了仅对于顶层的多个定义(最后的胜利)应该发生的情况。

相反,没有定义在嵌套部分中做什么,并且不同的实现(例如 Firefox 和 Chrome)也不一致。显然是无意义的,例如,在if-else语句的两个分支中放置同一函数的两个定义(请记住,名称绑定必须在作用域中的语句开始执行之前立即发生)。

只是不要那样做。