功能声明的评估顺序

时间:2017-12-05 07:22:39

标签: javascript

使用以下代码,既不是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

}

我会得到预期的结果。

3 个答案:

答案 0 :(得分:0)

在您的第一个声明中,您在foo()中附加了{},这会更改声明的范围,但下一个声明是全局范围。来自w3schools JavaScript Hoisting

  

吊装是JavaScript移动所有声明的默认行为   到当前范围的顶部

因此,您的第二个声明实际上已移至顶部,然后执行范围{}内的声明,该声明将覆盖第一个声明并打印a

答案 1 :(得分:0)

  

我原本期望'b'的结果,因为foo的第二个声明应该覆盖第一个。

不,这不是第二个声明应该覆盖第一个声明。预期结果将是'b',因为第一个声明包含在其自己的(块)范围内,不会影响外部范围。

  

但结果是'a'!为什么不'b'????

因为你没有在严格模式下执行代码(你真的应该!)。在草率模式下,Web浏览器behave weird与旧的引擎兼容,这些引擎专门处理ES5中当时非法的函数语句。

那么这里发生了什么?在计算语句时,块作用域函数声明会将函数分配给同名的顶级变量。 (除了正常的范围(和“悬挂”)声明之外,还会发生这种情况。)

因此,您的第一个示例就像

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

答案 2 :(得分:-1)

除非它们位于顶层,否则不支持在单个范围内具有相同功能的多个定义。

原因是例如:

之间存在很大差异
function square(x) { return x*x; }

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

其中在第一种情况下,名称square在开始时绑定到函数对象,而不是在定义是"执行时#34;在正常的程序流程中:

function foo() {
    console.log(square(12));  // Valid, no prob the function is defined "later"
    function square(x) { return x*x; }
}
然而,这意味着如果您在同一范围内放置不同的定义,则不清楚应该做什么。

该标准描述了仅针对多个定义在顶级(最后一次胜利)时会发生什么。

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

不要那样做。