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; } |
关于问题的第二部分:
函数声明
1 | function foo() { ... } |
由于函数提升,可以在定义之后和之前调用以这种方式声明的函数。
函数表达式命名函数表达式
1 | var foo = function bar() { ... } |
匿名函数表达式
1 | var foo = function() { ... } |
1 | (function() { ... }()); |
结论
Crockford建议使用函数表达式,因为它清楚地表明
关于第三个定义:
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; } |
虽然完全的区别更复杂,但我关心的唯一区别是机器创建函数对象的时候。在声明的情况下,在执行任何语句之前,但在调用语句体(即全局代码体或子函数)之后,以及在表达式的情况下,在执行语句的情况下。除此之外,对于所有的意图和目的,浏览器都是一样的。
为了帮助您理解,请看一下这个性能测试,它打破了我对内部声明的函数所做的假设,当调用外部函数时,这些函数不需要由机器重新创建。我也有点羞愧,因为我喜欢用这种方式编写代码。