函数表达式与JavaScript中的声明有什么区别?

What is the difference between a function expression vs declaration in JavaScript?

本问题已经有最佳答案,请猛点这里访问。

下面几行代码有什么区别?

1
2
3
4
5
6
7
8
//Function declaration
function foo() { return 5; }

//Anonymous function expression
var foo = function() { return 5; }

//Named function expression
var foo = function foo() { return 5; }
  • 什么是命名/匿名函数表达式?
  • 什么是声明函数?
  • 浏览器如何以不同的方式处理这些构造?

对类似问题(var functionname=function()vs function functionname())的回答不完全正确吗?


它们实际上很相似。调用它们的方式完全相同。区别在于浏览器如何将它们加载到执行上下文中。

在执行任何代码之前加载函数声明。

函数表达式只在解释器到达该行代码时加载。

因此,如果在加载函数表达式之前尝试调用它,将会得到一个错误!如果您改为调用函数声明,它将始终工作,因为在加载所有声明之前,不能调用任何代码。

示例:函数表达式

1
2
alert(foo()); // ERROR! foo wasn't loaded yet
var foo = function() { return 5; }

示例:函数声明

1
2
alert(foo()); // Alerts 5. Declarations are loaded before any code can run.
function foo() { return 5; }

关于问题的第二部分:

var foo = function foo() { return 5; }与另外两个完全相同。只是这一行代码曾经在Safari中引起错误,但现在已经不存在了。


函数声明

1
function foo() { ... }

由于函数提升,可以在定义之后和之前调用以这种方式声明的函数。

函数表达式

  • 命名函数表达式

    1
    var foo = function bar() { ... }
  • 匿名函数表达式

    1
    var foo = function() { ... }
  • foo()只能在创建后调用。

    立即调用的函数表达式(IIFE)

    1
    (function() { ... }());

    结论

    Crockford建议使用函数表达式,因为它清楚地表明foo是一个包含函数值的变量。嗯,就我个人而言,我更喜欢使用声明,除非有表达的理由。


    关于第三个定义:

    1
    var foo = function foo() { return 5; }

    下面是一个示例,演示如何使用递归调用的可能性:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    a = function b(i) {
      if (i>10) {
        return i;
      }
      else {
        return b(++i);
      }
    }

    console.log(a(5));  // outputs 11
    console.log(a(10)); // outputs 11
    console.log(a(11)); // outputs 11
    console.log(a(15)); // outputs 15

    编辑:更有趣的例子是闭包:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    a = function(c) {
     return function b(i){
      if (i>c) {
       return i;
      }
      return b(++i);
     }
    }
    d = a(5);
    console.log(d(3)); // outputs 6
    console.log(d(8)); // outputs 8


    第一条语句取决于声明它的上下文。

    如果在全局上下文中声明,它将创建一个名为"foo"的隐式全局变量,该变量将指向函数。因此,函数调用"foo()"可以在JavaScript程序的任何地方进行。

    如果函数是在一个闭包中创建的,它将创建一个名为"foo"的隐含局部变量,然后可以使用"foo()"在闭包内调用函数。

    编辑:

    我还应该说,函数语句(第一个)是在函数表达式(另一个2)之前解析的。这意味着,如果您在脚本的底部声明该函数,您仍然可以在顶部使用它。函数表达式只有在被执行代码击中时才会得到计算结果。

    结束编辑

    语句2和3几乎是等价的。同样,如果在全局上下文中使用,它们将创建全局变量;如果在闭包中使用,它们将创建局部变量。但是值得注意的是,语句3将忽略函数名,因此本质上可以调用函数任何东西。因此

    1
    var foo = function foo() { return 5; }

    是一样的

    1
    var foo = function fooYou() { return 5; }


    虽然完全的区别更复杂,但我关心的唯一区别是机器创建函数对象的时候。在声明的情况下,在执行任何语句之前,但在调用语句体(即全局代码体或子函数)之后,以及在表达式的情况下,在执行语句的情况下。除此之外,对于所有的意图和目的,浏览器都是一样的。

    为了帮助您理解,请看一下这个性能测试,它打破了我对内部声明的函数所做的假设,当调用外部函数时,这些函数不需要由机器重新创建。我也有点羞愧,因为我喜欢用这种方式编写代码。