64

可能重复:
JavaScript:var functionName = function() {} vs function functionName() {}

在 Javascript 中提取函数有两种可能的方法:

var foo = function() { ... }

这有点做作;另一个常见的模式是:

var foo = {
   baz: 43,
   doSomething: function() {
       // ...
   }
}

相对

function foo() { 
  // ... 
}

是否有明确的理由偏爱其中一个?

4

4 回答 4

87

这一切都取决于您声明函数的位置;吊装。

函数声明和变量声明总是被 JavaScript 解释器以不可见的方式移动(“提升”)到其包含范围的顶部。显然,函数参数和语言定义的名称已经存在。这意味着这样的代码:

function foo() {
    bar();
    var x = 1;
}

实际上是这样解释的:

function foo() {
    var x;
    bar();
    x = 1;
}

请注意,声明的赋值部分没有被提升。只有名字被吊起。这不是函数声明的情况,整个函数体也将被提升。

function test() {
    foo(); // TypeError "foo is not a function"
    bar(); // "this will run!"
    var foo = function () { // function expression assigned to local variable 'foo'
        alert("this won't run!");
    }
    function bar() { // function declaration, given the name 'bar'
        alert("this will run!");
    }
}
test();

在这种情况下,只有函数声明的主体被提升到顶部。名称 'foo' 被提升,但主体被留下,在执行期间被分配。

您可以为函数表达式中定义的函数命名,其语法类似于函数声明。这不会使其成为函数声明,并且名称不会被带入范围,主体也不会被提升。

foo(); // TypeError "foo is not a function"
bar(); // valid
baz(); // TypeError "baz is not a function"
bin(); // ReferenceError "bin is not defined"

var foo = function () {}; // anonymous function expression ('foo' gets hoisted)
function bar() {}; // function declaration ('bar' and the function body get hoisted)
var baz = function bin() {}; // named function expression (only 'baz' gets hoisted)

foo(); // valid
bar(); // valid
baz(); // valid
bin(); // ReferenceError "bin is not defined"

因此,如果您希望将函数提升到顶部,请使用function declarationelse use expression。我更喜欢后者,因为我通常使用 as 方法构建对象文字function expressions

function expressions当抛出错误时,命名会很方便。控制台会告诉您函数是什么,而不是说明anonymousaka stack trace

于 2012-04-10T00:57:49.613 回答
11

您在这里遇到了一些不同的事情,但我会先尝试解决您的主要问题。

一般来说....

function() { ... }是一个函数表达式2从语法上讲,这与or处于同一级别[4,5]。这代表一个。因此var foo=function(){ ... },每次都会按计划进行。

function foo() { ... } 是一个函数声明。这似乎与 做同样的事情var foo=function(){...},但有一个小警告。作为一个声明,它的工作方式类似于 JS 中变量提升的概念(基本上,所有变量声明都是在计算任何表达式之前完成的)。

一个很好的例子来自这里

function test() {
    foo(); // TypeError "foo is not a function"
    bar(); // "this will run!"
    var foo = function () { // function expression assigned to local variable 'foo'
        alert("this won't run!");
    }
    function bar() { // function declaration, given the name 'bar'
        alert("this will run!");
    }
}
test();

基本上变量提升已将值提升到顶部,因此此代码(理论上)等效于:

function test() {
    var foo;//foo hoisted to top
    var bar=function(){//this as well
        alert("this will run!");
    }

    foo(); // TypeError "foo is not a function"
    bar(); // "this will run!"
    var foo = function () { // function expression assigned to local variable 'foo'
        alert("this won't run!");
    }
}

注意:我想借此机会说 JS 解释器很难遵循理论,因此不建议在有些不确定的行为上相信他们。在这里,您会在一节的末尾找到一个很好的例子,理论和实践最终都不起作用(还有一些关于表达式与声明主题的更多细节)。

有趣的事实:function foo() {...}用括号括起来会将它从声明转换为表达式,这可能会导致一些看起来很奇怪的代码,例如

(function foo() { return 1; })();// 1
foo; //ReferenceError: foo is not defined

如果没有理由,请不要这样做。


摘要 var foo=function(){ ... }*sorta * 与 function 相同,foo(){ ... }只是前者在你认为它应该做的地方做你认为它应该做的事情,而后者做一些奇怪的事情,除非你将它包装在括号中,但这会弄乱范围,并且 JS 解释器允许你做一些在规范中被认为是语法错误的事情,所以你被引导相信错误的事情实际上是正确的,等等......

请使用函数表达式( var f=function(){...})。没有真正的理由不这样做,尤其是考虑到当您使用点语法时您有些被迫这样做。


关于你触及的第二件事......

我真的不知道该说什么,这有点与其他关于此的事情完全不同。

var foo = {
    baz: 43,
    doSomething:function() {
        ...
    }
}

这被称为对象字面量语法。基于这种语法的 JSON 是一种非常简洁的数据格式化方式, JS 中的这种语法通常用于声明新对象,例如单例对象(避免声明函数和使用 new 的所有混乱) . 它也可以以与使用 XML 相同的方式使用,并且是所有酷孩子的首选...

无论如何,基本上对象字面量语法是这样工作的:

{ name1: val1, .... namek:valk }

这个表达式是一个对象,上面初始化了某些值。这样做var obj={ name1: val1, .... namek:valk }意味着:

obj.name1==val1;
obj['name1']==val1;// x['y'] is the same thing as x.y 
...
obj.namek==valk;

那么这与我们的例子有什么关系呢?基本上,您的表达式通常用于声明单例对象。但是它也可以用来声明一个对象原型,所以以后有人可以做 var newObj=Object.create(foo) ,newObj 就会有 foo 作为原型。

如果您想真正了解原型继承的用途,请仔细研究它。道格拉斯·克罗克福德(Douglas Crockford )在他的众多演讲之一中详细讨论了它)。

于 2012-04-10T01:25:01.213 回答
2

命名函数没有什么好处

  • 元分析的名称。functionInstance.name会告诉你名字。
  • 更重要的是,名称将打印在堆栈跟踪中。
  • 名称还有助于编写自我记录或识字代码。

命名函数表达式有一个缺点

  • IE 存在 NFE 内存泄漏

除了较少的风格控制之外,函数声明没有任何缺点

于 2012-04-10T01:05:52.383 回答
1

您的问题实际上由两部分组成,因为如果将函数分配给变量或属性,您不一定必须将它们设为匿名。

命名与匿名?

@Raynos 清楚地强调了要点。命名函数最好的部分是它们将在堆栈跟踪中显示自己。即使在将函数分配给变量/属性的情况下,为函数命名以帮助调试也是一个好主意,但我不会说匿名函数根本是邪恶的。它们确实有很好的用途:

匿名函数在 JavaScript 中是一种不好的做法吗?

函数声明与函数表达式?

对于问题的那一部分,我建议您参考这个问题,因为它可能比我能更深入地涵盖该主题

var functionName = function() {} vs function functionName() {}

于 2012-04-10T01:10:34.390 回答