在条件语句中提升函数

Hoisting of functions inside conditionals

我了解 javascript 中的提升是如何发生的,函数在变量之前提升,并且只有声明被提升。但是当我遇到 if/else 条件句中的提升时,就像这个:

foo(); // "b is the output"
var a = true;

if (a) {
  function foo() { console.log("a"); }
}
else {
  function foo() { console.log("b"); }
}

现在条件为真,所以根据 if 块,a 应该是输出,但由于某种提升我假设 b 是输出。

那么 b 的输出如何?

在JavaScript中,变量、函数表达式和函数声明被提升到作用域的顶部。

函数声明定义一个命名函数变量而不需要变量赋值。

重要的是要知道函数声明的整个主体都提升了作用域。

例如

function outerFunction() {
    console.log(typeof functionDeclaration); // outputs "function"

    function functionDeclaration() {
        // ... function body
    }
}

这是因为,由于提升代码运行如下:

function outerFunction() {
    function functionDeclaration() {
        // ... function body
    }

    console.log(typeof functionDeclaration); // outputs "function"
}

在您的例子中,foo 的最后一个函数声明被提升到覆盖所有其他函数声明的范围的顶部。因此,它记录 "b".

然而,变量和函数表达式在没有指定值的情况下被提升。

例如

function outerFunction() {
    console.log(functionExpression); // outputs "undefined"      

    var functionExpression = function () {
        // ... function body
    }
}

运行更像这样,

function outerFunction() {
    var functionExpression = undefined;

    console.log(functionExpression); // outputs "undefined"

    functionExpression = function () {
        // ... function body
    }
}

(忽略某些旧浏览器可能具有的轻微狡猾行为:)

在 Javascript 中,函数语句的作用域在包含函数内(如果没有包含函数,则为全局),它们不在 if 或 else 或循环块内。所以你不能以那种方式 有条件地声明一个函数 (它可以用另一种方式完成;见下文)。如果您在同一范围内声明了多个同名函数,则后一个将覆盖第一个。

那么您的代码会发生什么:

  1. 两个函数语句都被提升了,但是

  2. 它们具有相同的名称,因此第一个被第二个覆盖。

  3. 变量 a 已创建但尚未赋值。

  4. 执行foo()语句,记录"b"

  5. a 被赋值为 true.

  6. 执行if。条件为真,但 if 和 else 分支实际上都没有做任何事情,因为除了之前提升的函数声明之外,它们不包含其他语句。

如果你想有条件地创建函数,你必须声明一个变量,然后为它分配一个函数表达式。然后在该赋值之后才能调用该函数:

var foo;
var a = true;

if(a)
    foo = function() { console.log("a"); };
else
    foo = function() { console.log("b"); };

foo();

这是一个未指定的行为,不同的浏览器行为不同。

MDN explain

在chrome&firefox中,会输出foo is not a function

而在safari中,会输出b.

another doc of MDN

这在现代 JS 浏览器中不再是正确的行为!现代浏览器将像这样编译和提升上述代码:

// hoisting (treat foo declarations as a normal variable declaration inside if statement)
var a;
var foo;

// now the assignment
foo(); // TypeError (foo = undefined)

a = true;

if (a) {
  function foo() { console.log("a"); }
}
else {
  function foo() { console.log("b"); }
}