【问题标题】:What is the difference between a function expression vs declaration in JavaScript? [duplicate]JavaScript中的函数表达式与声明有什么区别? [复制]
【发布时间】:2010-11-04 01:04:31
【问题描述】:

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

//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() {}) 的回答有哪些不完全正确的地方?

【问题讨论】:

标签: javascript


【解决方案1】:

它们实际上非常相似。调用方式完全一样。不同之处在于浏览器如何将它们加载到执行上下文中。

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

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

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

示例:函数表达式

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 中导致错误,但现在不会了。

【讨论】:

  • 最后一个和var foo = function() { return 5; }不一样。因为这里foo.name'',最后一个是'foo'
  • @JCM AFAIK,name 属性不是 ECMAScript 的一部分,仅在某些浏览器中实现。 Function.name at MDN
  • @ZachL 只是作为例子,我想说的是第二个函数有一个名字,而第一个没有。
  • "但是如果你调用一个函数声明,它总是可以工作的。"那么使用函数表达式有什么好处吗?为什么不总是使用声明?
  • 使用函数表达式实际上被认为是一种最佳实践,因为这样行为比使用声明更直观。它遵循逻辑流程,读起来更好,你定义它然后调用它,如果你没有得到一个错误,这是预期的行为。实际上我认为在非函数范围内不允许函数声明......我推荐这篇关于这个主题的帖子:javascriptweblog.wordpress.com/2010/07/06/…
【解决方案2】:

函数声明

function foo() { ... }

由于函数提升,这样声明的函数可以在定义前后调用。

函数表达式

  1. 命名函数表达式

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

    var foo = function() { ... }
    

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

Immediately-Invoked Function Expression (IIFE)

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

结论

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

【讨论】:

  • 欢迎来到 Stack Overflow!感谢您发布您的答案!请务必仔细阅读FAQ on Self-Promotion。另请注意,每次链接到自己的网站/产品时,都要求发布免责声明。
  • 兴趣点:js区分大小写。您的大写锁定示例不起作用;-)
  • 另外,您可以拥有一个命名的 IIFE:(function myFunc() { ... }());
  • 更短且广泛使用的 IIFE 编写方式:如果您不关心返回值,或者不关心使代码更难阅读的可能性,您可以通过在函数前添加一个字节来节省使用一元运算符。示例:!function(){ /*code*/ }();(来源:linked article
  • @naXa - +1 链接,关于 IIFE 的文章写得很好 :)
【解决方案3】:

关于第三个定义:

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

【讨论】:

  • 你不需要为函数声明一个不同的名字来使其递归。事实上,我会说这使事情变得混乱。 a = function a(i)return a(++i) 产生相同的结果
  • 但是为函数使用与变量不同的名称更清楚地说明了这一点。感谢您提供了一个使用命名函数表达式的示例。
【解决方案4】:

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

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

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

编辑:

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

结束编辑

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

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

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

【讨论】:

  • fooYou 不会被忽略。它在函数体中可见,因此函数可以引用自身(例如实现递归)。
  • 这很好。我没想到:)
  • 此外,命名函数表达式对于调试很有用: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) 或仅这些行,具体取决于您执行它的位置。
  • 命名函数表达式是推荐的做法,因为它允许您在需要时在内部引用函数。例如,递归调用函数或处理其名称或属性。顺便说一下,主要的好处是调试。如果您使用未命名的函数,则很难调试那里发生的事情,因为您将获得对匿名函数的引用而不是其名称
【解决方案5】:

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

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

【讨论】:

    猜你喜欢
    • 2012-12-31
    • 2017-08-13
    • 2016-05-15
    • 2014-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多