【问题标题】:Hoisting of functions inside conditionals在条件句中提升函数
【发布时间】:2016-05-16 23:47:53
【问题描述】:

我了解 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是怎么输出的呢?

【问题讨论】:

  • 需要考虑的事情:在您调用 foo() 时,if 尚未执行,因此尚未评估 if 条件。 (并且变量 a 还没有被赋值。)
  • @nnnnnn 它在日志中没有任何区别,它仍然显示 b ,当我使用立即调用的函数时,它确实按预期工作。
  • 尝试在 2018 年运行此函数,由于一些奇怪的 JS 部分,foo() 将输出 a

标签: javascript if-statement hoisting


【解决方案1】:

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

【讨论】:

  • 谢谢,这意味着;首先,带有 a 的 foo() 将被提升,然后带有 b 的 foo() 将被提升,因此将考虑后者并且 JS 引擎只有一个引用到一个函数 foo 所以它会执行它具有 console.log('b') 对吗?
  • 函数表达式没有被提升,我认为这可能会引起一些混乱,因为你说变量被提升但没有用函数表达式值初始化
【解决方案2】:

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

在 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();

【讨论】:

  • 不客气。请注意,var 也没有块作用域,它具有函数作用域,但较新的变量声明关键字 letconst do 具有块作用域。
  • @nnnnnn 这个程序的结果怎么样?
  • 你如何解释这个sn-p? ``` while(x
  • @Chao - 我没有,除了猜测(我现在没有时间研究)浏览器行为自两年前我写这个答案以来发生了变化。如果我在 IE 中运行它们,您的 sn-p(假设首先初始化 x)和 OP 的代码仍然具有我在答案中描述的行为,而 Chrome 和 FF 现在似乎有条件地根据 @987654331 重新定义函数@ 健康)状况。我认为我的回答的第一句话是指当时的旧版本IE,但我现在不记得了。
  • @MMiller - ECMA 规范已经向前发展,当前的浏览器不会像旧浏览器那样对待它。有关当前行为的更多信息,请参阅this answer to a similar question
【解决方案3】:

在现代 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"); }
}

【讨论】:

    【解决方案4】:

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

    MDN explain

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

    在 safari 中,它会输出b

    another doc of MDN

    【讨论】:

      猜你喜欢
      • 2021-01-04
      • 1970-01-01
      • 2014-02-19
      • 1970-01-01
      • 2015-10-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多