在JavaScript中声明函数

105

这两种声明函数的方式有什么区别?

function someFunc() { ... }

var someFunc = function() { ... }

我并不是在技术上提问。我不是在询问哪种更易读,或者哪种风格更受欢迎。

8个回答

49

我和这里的大多数人持不同意见。从技术上讲,以两种方式声明函数可能意味着相同的语法 (我对我的上一句话不正确。我在另一篇文章中读到了为什么它们在技术上不同,我会在最后添加说明。), 但是它们在演变模式方面发挥的作用是巨大的。我强烈推荐Douglas Crockford的《JavaScript高级程序设计》。

但为了以微妙和简单的方式证明我的观点,这里有一个小例子。

//Global function existing to serve everyone
function swearOutLoud(swearWord) {
    alert("You "+ swearWord);           
}
//global functions' territory ends here

//here is mr. spongebob. He is very passionate about his objects; but he's a bit rude.
var spongeBob = {
    name : "squarePants",
    swear : function(swearWord) {
                name = "spongy";
                alert("You "+ swearWord);
                return this;
            }
}

//finally spongebob learns good manners too. EVOLUTION!
spongeBob.apologize = function() {
    alert("Hey " + this.name + ", I'm sorry man!");
    return this;
}


//Ask spongebob to swear and then apologize in one go (CASCADING EFFECT!!)
alert(spongeBob.swear("twit").apologize());
如果您查看上面的代码,我声明了一个名为swearOutLoud的函数。它可以从任何对象或调用中获取脏话,并给出输出。它可以使用传递给它的"this"参数和参数对任何对象执行操作。
但是第二个声明被声明为名为"spongeBob"的对象的属性。这很重要;因为我正在向对象驱动的行为过渡。虽然我还保持着"级联效应",如果我没有其他东西要返回,就会返回"this"。
在jquery中也做了类似的事情;如果你想编写框架或其他东西,这种级联模式非常重要。您还将将其与生成器设计模式相关联。
但是,通过将函数声明为对象的属性,我能够实现以对象为中心的行为,这导致更好的编程范式。除非设计良好;具有全局访问权限的单个函数在外部声明会导致非面向对象的编码方式。我更喜欢后者。
要查看级联效果,请查看最后一条语句,您可以要求spongebob同时发誓和道歉;即使后来添加了道歉作为属性。
我希望我的观点很清楚。从技术角度来看,差异可能很小;但从设计和代码演变的角度来看,它是巨大的,产生了巨大的差异。
但这只是我!接受还是拒绝。 :)
编辑:
因此,这两个调用在技术上是不同的;因为命名声明与全局命名空间绑定,并在解析时定义。因此,即使在函数声明之前也可以调用。
 //success
 swearOutLoud("Damn");

function swearOutLoud(swearWord) {
    alert("You " + swearWord)
}

上面的代码将正常工作。但下面的代码不会。

swear("Damn!");
    var swear = function(swearWord) {
    console.log(swearWord);
}

28
重点部分不清晰,与问题无关。 - Tim Down
也许,但另一方面,海绵宝宝正在和你说话。 - Mozgor

14

使用 function someFunc() { ... } 的一个优点是函数名称会在 Firebug 调试器中显示。以另一种方式声明的函数(var someFunc = function() { ... })会显示为匿名


5

就样式而言,第二个示例与其他常见的函数声明方式更加一致,因此可以认为它更易读。

this.someFunc = function() { ... }
...
someFunc: function() { ... },

然而,正如之前提到的那样,它是匿名的,因此在分析时不会显示名称。 另一种声明函数的方法如下,可以让你同时拥有最好的两个世界。

var someFunc = function someFunc() { ... }

5

实际上,两者的区别在于第二种声明方式使我们能够像这样声明函数,从而使将函数作为对象属性成为可能:

var myObject=new Object();
myObject.someFunc=function() { ... }; 

2

另一个区别是,在大多数浏览器上,后者允许您根据情况定义不同的实现,而前者则不会。假设您想要跨浏览器事件订阅。如果您尝试这样定义一个addEventListenerTo函数:

if (document.addEventListener) {
    function addEventListenerTo(target, event, listener) {
        ....
    }
} else if (document.attachEvent) {
    function addEventListenerTo(target, event, listener) {
        ....
    }
} else {
    function addEventListenerTo(target, event, listener) {
        ....
    }
}

在某些浏览器上,所有函数都会被解析,最后一个函数优先。结果:以上代码无法正常工作。然而,将匿名函数赋值给变量将有效。您也可以使用分配给变量的匿名函数应用功能和基本面向方面编程技术。
var fib = memoize(function (n) { 
    if (n < 0) return 0;
    if (n < 2) return 1;
    return fib(n-1) + fib(n-2); 
});

...
// patch the $ library function
if (...) {
    $ = around($, fixArg, fixResult);
}

2

它同样正确,第一形式为:

function test() { }

是一种更为常见的语法形式,而第二种形式:

var test = function() { ... }

通过使用var,允许您控制函数的范围(没有它,它将是全局的)。

而且你甚至可以两者都做:

var test = function test() { ... test(); ... }

这使您能够在第二种形式中定义递归函数。


1
我不同意第一种形式是更被认可的语法,而第二种形式不是。它们只是不同的形式,都是常用的。此外,在IE中,命名函数表达式(这是您在最后一个示例中使用的内容)具有奇怪的行为。请参阅以下文章:http://yura.thinkweb2.com/named-function-expressions/ - Tim Down

1

当你写代码时

function Test() {
}

JavaScript 实际上是创建一个属性,将函数对象分配给该属性,一旦调用该函数对象,就会执行函数定义中报告的代码。该属性附加到对象 window 或包含函数定义的对象。


1
你在这里有一些误解:首先,在JavaScript中,命名函数和匿名函数之间存在差异,可以通过调用toString函数来看到。其次,虽然函数声明确实会向当前变量对象添加属性,但这仅适用于全局代码(例如,在函数内部而不是全局代码中)。因此,你的第三段完全是错误的。(续...) - Tim Down
1
第三,函数赋值给变量对象的时机也有所不同。使用函数声明创建的函数(例如 function test() {})可以在出现在其前面的代码中使用,而使用函数表达式声明的函数则不行(例如 var test = function() {};)。 - Tim Down
1
这与《JavaScript权威指南》中所述不同,该书明确指出:“当JavaScript解析器遇到函数定义时,它会解析并存储(而不执行)组成函数主体的内容。然后,它会定义一个属性(如果函数定义嵌套在另一个函数中,则在调用对象中;否则,在全局对象中)与函数同名以保存该函数。” - apaderno
考虑到我所报告的是由解析器完成的,我不明白为什么我所报告的不是真实的。如果解析器允许编写"lambda x(//...}",并将其转换为普通函数声明,那么这是否意味着JavaScript支持lambda函数?或者,如果一种语言接受一行代码"o = object[0, 23]",但解析器将其更改为"o = array[0, 23]",这是否意味着该语言支持对象? - apaderno
JavaScript解析器在遇到函数声明或表达式时应该做什么(实际上大多数浏览器都是这样做的)在ECMAScript规范中已经明确定义。影响用户的部分是,代码可以引用在源代码中出现在其后面的函数声明。而且函数声明并不会创建一个匿名函数对象:函数有一个名称,在函数的toString()方法的输出中显示,并且在某些实现中(例如Mozilla浏览器)可用作函数的name属性。 - Tim Down
显示剩余4条评论

1

为了可读性,我认为第一个显然更好。未来的维护程序员,即使假设他们足够熟悉JavaScript以知道本主题中出现的许多细节,也会假定第一种格式。

例如,如果他们有一天想要使用ctrl-f搜索您的函数定义以查看其中发生了什么,他们会首先搜索someFunc = function()还是function someFunc()

此外,为了变得更加排版(因为我们正在谈论可读性),读者通常会快速扫描文本,并且如果他们正在寻找函数定义,则更倾向于跳过以“var”开头的行。

我知道这是一个非技术性的答案,但对于人类来说,阅读代码比计算机更难。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接