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

IT技术 javascript
2021-01-31 00:22:51

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

//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() {} 与 function functionName() {})的回答不完全正确?

5个回答

他们其实真的很像。你如何调用它们是完全一样的。不同之处在于浏览器如何将它们加载到执行上下文中。

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

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

因此,如果您尝试在加载函数表达式之前调用它,则会出现错误!如果您改为调用函数声明,它将始终有效,因为在加载所有声明之前无法调用任何代码。

示例:函数表达式

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

示例:函数声明

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


至于你问题的第二部分:

var foo = function foo() { return 5; }真的和其他两个一样。只是这行代码曾经在 safari 中导致错误,尽管现在不再这样做了。

@JCM AFAIK,name 属性不是 ECMAScript 的一部分,仅在某些浏览器中实现。Function.name在 MDN
2021-03-10 00:22:51
@ZachL 仅用作示例,我想说的是第二个函数有一个名称,而第一个函数没有。
2021-03-15 00:22:51
使用函数表达式实际上被认为是最佳实践,因为这样的行为比使用声明更直观。当它遵循逻辑流程时,它读起来更好,你定义它然后调用它,如果你不这样做,你会得到一个错误,这是预期的行为。实际上我认为函数声明在非函数作用域中是不允许的……我推荐这篇关于这个主题的帖子:javascriptweblog.wordpress.com/2010/07/06/……
2021-03-29 00:22:51
最后一个不一样var foo = function() { return 5; }因为这里foo.name'',最后一个是'foo'
2021-03-31 00:22:51
“但是如果你调用一个函数声明,它总是有效的。” 那么使用函数表达式有什么好处吗?为什么不总是使用声明?
2021-04-07 00:22:51

函数声明

function foo() { ... }

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

函数表达式

  1. 命名函数表达式

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

    var foo = function() { ... }
    

foo() 只能在创建后调用。

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

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

结论

Crockford 建议使用函数表达式,因为它明确表示foo是包含函数值的变量。好吧,就我个人而言,除非有原因,否则我更喜欢使用声明。

欢迎使用堆栈溢出!感谢您发布您的答案!请务必仔细阅读有关自我推销常见问题解答另请注意,每次链接到您自己的网站/产品时都需要发布免责声明。
2021-03-12 00:22:51
@naXa - 链接 +1,关于 IIFE 的文章写得很好 :)
2021-03-12 00:22:51
兴趣点:js 区分大小写。您的大写锁定示例不起作用;-)
2021-03-14 00:22:51
编写 IIFE 的更短且广泛使用的方法:如果您不关心返回值,或者使您的代码更难阅读的可能性,您可以通过在函数前加上一元运算符来保存一个字节。示例:( !function(){ /*code*/ }();来源:链接文章
2021-03-16 00:22:51
此外,你可以有一个命名的 IIFE:(function myFunc() { ... }());
2021-03-26 00:22:51

关于第三个定义:

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

这是一个示例,它显示了如何使用递归调用的可能性:

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

编辑:更有趣的闭包示例:

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
但是为函数使用与变量不同的名称更清楚地说明了这一点。感谢提供命名函数表达式的使用示例。
2021-03-26 00:22:51
您不需要使用不同的名称来声明函数以使其递归。事实上,我会说这令人困惑。a = function a(i)和做return a(++i)产生相同的结果
2021-03-28 00:22:51

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

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

如果函数是在闭包中创建的,它将创建一个名为“foo”的隐式局部变量,然后您可以使用该变量通过“foo()”调用闭包内的函数

编辑:

我还应该说函数语句(第一个)在函数表达式(其他两个)之前被解析。这意味着如果您在脚本底部声明该函数,您仍然可以在顶部使用它。函数表达式仅在被执行代码命中时才会被评估。

结束编辑

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

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

是相同的

var foo = function fooYou() { return 5; }
此外,命名函数表达式对于调试很有用:var foo = function fooYou() { return 5; }; console.log(foo); console.log(foo.name);将打印fooYou() / fooYou(Firefox)、[Function: fooYou] / fooYou(node.js)、function fooYou() { return 5; } / fooYou(Chrome) 或单独这些行,具体取决于您执行它的位置。
2021-03-14 00:22:51
那是个很好的观点。我没想到:)
2021-03-16 00:22:51
命名函数表达式是推荐的做法,因为它允许您在需要时在内部引用函数。例如,递归调用函数或处理其名称或属性。顺便说一下,主要的好处是调试。如果您使用未命名的函数,则很难在那里发生某些事情时进行调试,因为您将获得对匿名函数的引用而不是其名称
2021-04-03 00:22:51
fooYou不被忽视。它在函数体中可见,因此函数可以引用自身(例如实现递归)。
2021-04-07 00:22:51

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

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