javascript 函数声明的求值顺序

vvppvyoh  于 2023-04-28  发布在  Java
关注(0)|答案(3)|浏览(98)

对于下面的代码,它既不是ES6,也不是在“严格模式”下,我期望的结果是'B',因为foo的第二个声明应该覆盖第一个声明。但结果是A!

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

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

foo(); // 'a' ? Why not 'b'?

当这段代码用额外的花括号括起来时,结果是预期的'b'。

{ // additional curly braces

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

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

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

} // end additional curly braces

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

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

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

// from outside block :before declaration
// from outside block :after declaration

我无法在这里找到我的误解。
如果我再次将完整的最后一个例子括在花括号中,像这样:

{
  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
}

我得到了预期的结果。

smdncfj3

smdncfj31#

在第一个声明中,您将foo()包含在{}中,这将更改声明的范围,但下一个声明是全局范围。从w3schools JavaScript Hoisting
提升是JavaScript的默认行为,它将所有声明移动到当前作用域的顶部
因此,您的第二个声明实际上被移到顶部,然后{}范围内的声明执行,它覆盖第一个声明并打印a

8nuwlpux

8nuwlpux2#

我本来期望的结果是'B',因为foo的第二个声明应该覆盖第一个声明。
不,这并不是说第二个声明应该覆盖第一个声明。预期的结果将是'b',因为第一个声明包含在它自己的(块)范围内,不会影响外部范围。
但结果是A!为什么不是“B”????
因为您没有以严格模式执行代码(您确实应该这样做!)。在草率模式下,Web浏览器behave weird将与旧的引擎兼容,这些引擎专门处理ES 5中当时非法的函数语句。
这是怎么回事块作用域的函数声明在计算语句时将函数分配给同名的顶级变量。(这是在正常的作用域(和“提升”)声明之外发生的)。
因此,您的第一个示例的行为如下

var foo₀; // the top-level implicit introduction of the inner declaration
var foo₀ = function foo() { console.log('b'); } // the hoisted declaration
foo()₀; // logs 'b'
{
  let foo₁ = function foo() { console.log('a'); } // the (hoisted) inner declaration
  foo₀ = foo₁; // the weirdness!
  // a call to `foo()` in here would get you foo₁ from the local scope
}
foo()₀; // logs 'a'

第二个示例的行为如下

var foo₀; // the top-level implicit introduction of the first declaration
var foo₀; // the top-level implicit introduction of the second declaration
{
  let foo₁ = function foo() { console.log('b'); } // the (hoisted) second declaration
  foo()₁; // logs 'b'
  {
    let foo₂ = function foo() { console.log('a'); } // the (hoisted) first declaration
    foo₀ = foo₂; // the weirdness!
    // a call to `foo()` in here would get you foo₂ from the local scope
  }
  foo₀ = foo₁; // the weirdness!
  foo₁() // logs 'b' (as expected) - a call in here does get you foo₁ from the local scope
}
// a call to `foo()` out here would get you foo₀ from the top scope
eimct9ow

eimct9ow3#

不支持在单个范围中对同一函数进行多个定义,除非它们位于顶层。
原因是有很大的区别,例如:

function square(x) { return x*x; }

var square = function(x) { return x*x; }

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

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

然而,这意味着,如果你把不同的定义放在同一个作用域中,就不清楚应该怎么做了。
该标准描述了仅针对多个定义在顶层(最后获胜)应该发生的事情。
在嵌套部件中要做什么没有定义,并且不同的实现(例如,Firefox和Chrome)不同意。例如,将同一函数的两个定义放在if-else语句的两个分支中显然是无意义的(记住,名称绑定必须在作用域中的语句开始执行之前立即发生)。
别那么做

相关问题