“function foo(){}”和“foo = function(){}”有什么区别?

31
2个回答

37
不,它们并不相同,尽管它们都会导致你可以通过符号foo调用的一个函数。一个是函数声明,另一个是函数表达式。它们在不同的时间进行求值,在定义它们的作用域上产生不同的影响,并且在不同的地方是合法的。
引用我在这个其他问题的回答(为了相关性进行了一些编辑),以防那个问题因某种原因被删除(并且为了让人们节省跟随链接的时间):

JavaScript有两个不同但相关的东西:函数声明和函数表达式。它们之间有明显的区别:

这是一个函数声明

function foo() {
    // ...
}

在执行任何逐步代码之前,函数声明将在进入封闭范围时进行评估。 函数的名称(foo)将添加到封闭范围中(技术上是定义该函数的执行上下文的变量对象)。

这是一个函数表达式(具体来说,是匿名的,就像您引用的代码):

var foo = function() {
    // ...
};

函数表达式会在代码逐步执行时被计算,出现在它们所在位置(就像其他任何表达式一样)。下面的代码创建了一个没有名称的函数,并将其赋值给变量foo

函数表达式也可以被命名而不是匿名。一个命名函数看起来像这样:

var x = function foo() {  // Valid, but don't do it; see details below 
    // ...
};

根据规范,命名函数表达式应该是有效的。它应该创建一个名为foo的函数,但不应该将foo放在封闭作用域中,然后在遇到步骤性代码中的表达式时将该函数分配给变量x。当我说它不应该将foo放在封闭作用域中时,我的意思就是确切地这样:

var x = function foo() {
    alert(typeof foo); // alerts "function" (in compliant implementations)
};
alert(typeof foo);     // alerts "undefined" (in compliant implementations)

注意,这与函数声明的工作方式不同(其中函数名称将添加到封闭范围)。
命名函数表达式在兼容实现上工作正常,但以前存在于某些浏览器的几个错误,特别是 Internet Explorer 8 及更早版本(和一些早期版本的 Safari)。IE8 将命名函数表达式处理两次:首先作为函数声明(进入执行环境时),然后稍后作为函数表达式,在此过程中生成两个不同的函数。(真的。)
更多内容请参见:Double take此处
注意:下面的内容是在2011年撰写的。在2015年,控制块中的函数声明被添加到 ECMAScript 2015 中作为其一部分。它们的语义因您是否处于严格或松散模式以及松散模式下的环境是否为 Web 浏览器而异。当然,还取决于您使用的环境对于 ES2015 定义的支持是否正确。(令我惊讶的是,截至本文撰写的 2017 年 7 月,Babel 也无法正确地转译它们。)因此,只有在特定情况下才能可靠地在控制流结构中使用函数声明,因此现在仍然最好使用函数表达式。
function bar(x) {
    var foo;

    if (x) {
        foo = function() {  // Function expression...
            // Do X
        };
    }
    else {
        foo = function() {  // ...and therefore legal
            // Do Y
        };
    }
    foo();
}

但是这个东西实际上并不会像大多数实现所显示的那样做:

function bar(x) {

    if (x) {
        function foo() {  // Function declaration -- INVALID
            // Do X
        }
    }
    else {
        function foo() {  // INVALID
            // Do Y
        }
    }
    foo();
}

而且这也很有道理:由于foo函数声明在进入bar函数时被评估,在任何逐步执行代码之前,解释器不知道要评估哪个foo。这对表达式不是问题,因为它们在控制流期间完成。
由于语法无效,实现可以自由处理。我从未遇到过按我预期的方式抛出语法错误并失败的实现,几乎所有实现都会忽略控制流语句,并像在顶层有两个foo函数声明时应该做的那样进行操作(使用第二个; 这在规范中)。因此只使用第二个foo。 Firefox的SpiderMonkey是杰出的,它似乎(有效地)将它们转换为表达式,因此它使用取决于x的值。 实时示例

5
那个...是非常清晰的解释。 - RHSeeger
恐怕这有点过时了。在控制流块中使用函数声明的最新示例在Chrome(59)中运行良好。 - Shrike
@Shrike:确实,控制块中的声明在ECMAScript 2015中被编码。但是它们的语义取决于A)您是否使用严格模式或松散模式,以及B)在松散模式下,环境是否为Web浏览器。因此,我建议仅在严格模式下(如果有必要)使用它们,在那里它们在支持ES2015的所有环境中都是一致且简单明了的。 - T.J. Crowder
同意,还应该注意,在ES2015严格模式下,块级函数声明仅在该块内可见-https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions - Shrike
@Shrike:没错。它们被有效地转换为带有 let 的函数表达式。 - T.J. Crowder

1

1
我阅读了你提供的答案。现在我必须删除我自己的回答。每天都会学到新东西。 - nfechner
1
哦,嘿,没看到你之前链接了我的回答。谢谢! - T.J. Crowder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接