任何正文都可以向我提出一些使用内联函数来反对将预定义函数名称传递给某个处理程序的参数。
即。哪个更好:
(function() {
setTimeout(function() { /*some code here*/ }, 5);
})();
与
(function() {
function invokeMe() {
/*code*/
}
setTimeout(invokeMe, 5);
})();
奇怪的问题,但我们几乎在团队中争论这件事。
答案 0 :(得分:51)
命名函数
本页的问答中有一些严重滥用术语的行为。没有关于函数是否为内联函数(函数表达式)的信息,说明你无法命名它。
这是使用函数表达式:
setTimeout(function doSomethingLater() { alert('In a named function.'); }, 5);
这是使用功能声明:
function doSomethingLater() { alert('In a named function.'); }
setTimeout(doSomethingLater, 5);
这两个示例都使用了命名函数,并且在调试和分析工具方面都获得了相同的好处!
如果指定了名称(“函数”之后但在括号之前的文本),那么它是一个命名函数,无论它是内联还是单独声明。如果未指定名称,则为“匿名”。
注意:T.J。指出IE错误地处理了以非平凡的方式命名的函数表达式(参见:http://kangax.github.com/nfe/#jscript-bugs),这一点很重要,我只是想对术语提出一点意见。
您应该使用哪种?
在回答您的直接问题时,如果可以从代码中的任何其他位置使用该函数,则应使用命名函数语句。如果函数只在一个地方使用并且在其他任何地方没有相关性,那么我会使用一个函数表达式,除非它过长或者感觉不合适(出于样式原因)。如果使用内联函数表达式,那么为了调试或代码清晰度的目的,无论如何命名它通常很有用。
内存泄漏
无论是命名函数,使用函数语句还是使用函数表达式都对内存泄漏问题影响不大。让我试着解释导致这些泄漏的原因。看看这段代码:
(function outerFunction() {
var A = 'some variable';
doStuff();
})();
在上面的代码中,当“outerFunction”完成“A”超出范围并且可以进行垃圾回收时,释放该内存。
如果我们在那里添加一个函数怎么办?
(function outerFunction() {
var A = 'some variable';
setTimeout(function(){ alert('I have access to A whether I use it or not'); }, 5);
})();
在这段代码(上图)中,我们传递给setTimeout的函数表达式引用了“A”(通过闭包的魔力),甚至在“outerFunction”结束后“A”将保留在内存中,直到触发超时并取消引用该功能。
如果我们将该函数传递给setTimeout以外的其他内容怎么办?
(function outerFunction() {
var A = 'some variable';
doStuff(function(){ alert('I have access to A whether I use it or not'); });
})();
function doStuff(fn) {
someElement.onclick = fn;
}
现在我们传递给“doStuff”的函数表达式可以访问“A”,甚至在“outerFunction”完成之后“A”将保留在内存中,只要有对我们传递的函数的引用进入doStuff 。在这种情况下,我们正在创建对该函数的引用(作为事件处理程序),因此“A”将保留在内存中,直到该事件处理程序被清除。 (例如某人致电someElement.onclick = null
)
现在看看当我们使用函数语句时会发生什么:
(function outerFunction() {
var A = 'some variable';
function myFunction() { alert('I have also have access to A'); };
doStuff(myFunction);
})();
同样的问题!只有当“doStuff”没有对其进行引用时才会清除“myFunction”,并且只有在清除“myFunction”时才会清除“A”。无论我们使用的是陈述还是表达,都无关紧要;重要的是如果在“doStuff”中创建了对该函数的引用!
答案 1 :(得分:11)
两者之间存在一个的显着差异:后者有一个名称。
我喜欢帮助我的工具帮助我,因此我大多数avoid anonymous functions因为我的工具无法向我提供有关它们的有意义的信息(例如,在调试器中的调用堆栈列表中等)。
所以我选择了(function(){
function invokeMe() {
/*code*/
}
setTimeout(invokeMe, 5);
})();
...一般来说。但是,规则应该被打破,而不是盲目地屈服于。 : - )
请注意,根据规范,还有第三种选择:您可以使用也具有名称的内联函数:
(function(){
setTimeout(function invokeMe(){ /*some code here*/ }, 5);
})();
问题是,到目前为止,Microsoft的JavaScript解释器(“JScript”)的每个版本,包括(惊人地)IE9中的那个,都错误地处理命名函数表达式并创建两个在不同时间完全不同的功能。 (Proof,在IE9或更早版本以及几乎任何其他浏览器中尝试。)IE以两种方式弄错:1。它创建两个独立的函数对象,以及2.其中一个的结果,它将名称符号“渗透”到表达式的封闭范围内(明显违反规范的Section 13)。详情请见Double take
答案 2 :(得分:5)
IMO,声明一个函数只有在你打算以其他方式重新使用它时才会有用。
我个人使用setTimeout
处理程序的函数表达式(第一种方式)。
但是,您可能想知道函数声明和函数表达式之间的差异,我建议您阅读以下文章:
答案 3 :(得分:3)
我建议在对立的团队成员之间进行一场完整的决斗来解决这些争论。
更严重的是,最终它无关紧要。第一种形式(非命名函数)往往会因较大的函数而变得笨拙,但对于小(1-2行)函数来说并不是什么大不了的事。第二种形式同样无害。
任何针对这两种风格的论据都是纯粹的bikeshedding,imo。
答案 4 :(得分:2)
内联函数可避免名称空间污染,预定义函数具有更高的重用率。我认为你可以制作适合的案例。
答案 5 :(得分:1)
我认为像这样的代码的唯一区别是,使用第二段代码可以重新调用相同的函数(有时使用“定时器函数”它很有用):
(function(){
function invokeMe() {
if(..) setTimeout(invokeMe, 5);
}
setTimeout(invokeMe, 5);
})();
答案 6 :(得分:1)
我们都不能相处吗?
(function(){
setTimeout( (function InvokeMe(){ /*some code here*/ }), 5);
})();
只有一件事真的很重要,IMO和调试的简易性。很多步骤跟踪器都不能告诉你关于func的任何信息,除了它是匿名的并且有args但是你仍然可以通过将定义放在parens来强制评估来定义内联名称。对于非常简单或明显的突破性功能,我认为这不是什么大问题,但对我而言,它就像半决赛。如果它没有引起疼痛,我真的不在乎另一个人做了什么,但我总是试着命名我的功能,因为它并不难,这可能是一个优势。
答案 7 :(得分:1)
我知道,这是一个老问题, 但对我来说,有一个比已经提到的更重要的区别。 吊装 必须创建每个函数,因此在内存中保留一些空间,最后必须是GC。
命名函数被提升到周围函数的开头,因此在每次函数调用时都会被创建,无论它们是否被使用。 只有在执行定义它们的代码时才会创建匿名函数。
//an example where you wold prefer to use an anonymous function.
//you can assign this (anonymous) function to a variable, so you get your "name" back.
function someFn(){
if(condition){
//the variable declaration has been hoisted,
//but the function is created at this point, and only if necessary.
var process = function(value){/* */};
switch(condition2){
case 1: process(valueFor1); break;
case 2: process(valueFor2); break;
/* ... */
}
}
}
function someFn(){
var process;
if(condition){
process = function(value){ /* A */ }
}else{
process = function(value){ /* B */ }
}
//beware, depending on your code, "process" may be undefined or not a function
process(someValue);
}
//an example where you would prefer (/ utilize) the hoisting.
function someFn(){
/* some code */
while(condition){
//some might want to keep the function definition near the code where it is used,
//but unlike an anonymous function or a lambda-expression this process-function
//is created only once per function-call, not once per iteration.
function process(value, index){ /* ... */ }
/* ... */
process(value, index)
}
}
所以,根据经验:
应该没有匿名函数或lambda-expression
如果你只需要一个(很少真实的)条件,你应该更喜欢匿名函数而不是命名函数,因为它们只在需要时创建
如果你知道你的商业(JavaScript),你知道何时忽略这个建议
答案 8 :(得分:1)
预定义的命名函数可以减少JavaScript回调地狱问题,这在http://callbackhell.com/提到
答案 9 :(得分:0)
没有技术理由偏爱一个版本而不是另一个版本。对我来说通常取决于两件事:
示例:
setTimeout(function() { // I need to scroll to see the other arguments
// many lines of code
}, 0); // <- where does this '0' belong to?
答案 10 :(得分:0)
在提供的示例中,函数的声明和使用非常接近,我认为唯一的区别是可读性。我更喜欢第二个例子。
答案 11 :(得分:0)
我更喜欢使用命名函数。命名函数在所有调试器(air,firebug,IE)上按名称显示。
示例:
请注意,您还可以使用内联命名函数,例如
{
method: function obj_method(){}
}
这样,当你查看堆栈跟踪时,你会看到函数obj_method而不是匿名函数。
您是否询问何时内联函数而不是声明它?当它在代码中有意义时。如果你需要从两个不同的地方,它不能是内联的。有时内联使代码更容易阅读,有时更难。
答案 12 :(得分:0)
我也倾向于命名函数。匿名函数refs很快,但应该只用于简单的东西。我的经验法则是,如果函数超过2行代码,它可能属于它自己的定义。
大多数使用匿名函数的示例代码都很复杂。但样本通常非常简单。随着事情变得更加复杂,这种方法就会崩溃。我已经看到函数refs嵌套在函数refs中,因为开发人员意识到后续步骤中需要更多的回调。而不是这种基于树的逻辑,我更喜欢组织孤立的函数。
通常我很高兴能够重用我之后定义的一个函数。
匿名函数的一个重要用途是当你需要将范围数据传递给函数调用时,但我通常只是将我的函数包装到匿名函数中。
如果您进入测试驱动开发,那么命名函数也是绝对必要的。