Javascript:内联函数vs预定义函数

任何机构可以抛出一些使用内联函数来传递预定义的函数名称给一些处理程序的参数。

即哪个更好:

(function(){ setTimeout(function(){ /*some code here*/ }, 5); })(); 

 (function(){ function invokeMe() { /*code*/ } setTimeout(invokeMe, 5); })(); 

奇怪的问题,但我们几乎在这个团队中的战斗

命名的function

在这个页面的问题和答案中有一些严重的术语滥用。 没有关于函数是否内联(函数expression式),说你不能命名它。

这是使用一个函数expression式

 setTimeout(function doSomethingLater() { alert('In a named function.'); }, 5); 

这是使用函数声明

 function doSomethingLater() { alert('In a named function.'); } setTimeout(doSomethingLater, 5); 

这两个例子都使用了命名函数,并且在debugging和分析工具方面都有相同的好处!

如果指定了名称(“函数”之后但在括号之前的文本),那么它是一个命名函数,无论它是内联还是单独声明。 如果名字没有被指定,那么它是“匿名的”。

注意:TJ指出,IE不正确地处理命名函数expression式(请参阅: http : //kangax.github.com/nfe/#jscript-bugs ),这一点很重要,我只是试图对术语做一点说明。

你应该使用哪一个?

在回答你的直接问题时,如果你的代码中的任何地方都可以使用该函数,你应该使用一个命名的函数语句。 如果函数正好在一个地方使用,而且在其他地方没有任何关联,那么我会使用一个函数expression式,除非它过长或者感觉不合适(出于样式的原因)。 如果您使用内联函数expression式,那么为了debugging或代码清晰的目的,将其命名总是很有用的。

内存泄漏

无论你给函数命名,使用函数语句,还是使用函数expression式,对内存泄漏问题都没有什么影响。 让我试着解释造成这些泄漏的原因。 看看这个代码:

 (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的函数expression式引用了“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”的函数expression式可以访问“A”,甚至在“outerFunction”完成之后,只要有一个对我们传入doStuff的函数的引用,“A”就会留在内存中 。 在这种情况下,我们正在创build对该函数的引用(作为事件处理程序),因此“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”。 我们是否使用了一个声明或一个expression式并不重要; 重要的是如果在“doStuff”中创build了该函数的引用!

两者之间有一个显着的区别:后者有一个名字。

我喜欢帮助我的工具帮助我,所以我主要是避免匿名函数,因为我的工具不能给我有关它们的有意义的信息(例如,在debugging器中的调用堆栈列表等)。 所以我会去的

 (function(){ function invokeMe() { /*code*/ } setTimeout(invokeMe, 5); })(); 

一般来说 规则是打算被打破,虽然,并没有奴仆鞠躬。 🙂

请注意,根据规范,还有第三种select:您可以有一个内联函数也有一个名称:

 (function(){ setTimeout(function invokeMe(){ /*some code here*/ }, 5); })(); 

然而,问题在于微软的JavaScript解释器(“JScript”)的所有版本(包括令人惊讶的)在IE9中的版本都不正确地处理该命名的函数expression式,并在不同的时间创build两个完全不同的函数。 ( certificate ,在IE9或更早的版本,以及任何其他浏览器中都可以使用它。)IE的错误有两种:1.创build两个独立的函数对象,2.由于其中一个,它会“stream血“的名称符号包含在expression式的封闭范围内(明显违反了规范第13条的规定)。 详情在这里: 双重采取

国际海事组织,只有当你打算以其他方式重新使用它时,声明一个函数将是有用的。

我个人使用函数expression式(第一种方式)为setTimeout处理程序。

不过,您可能想知道函数声明和函数expression式之间的区别 ,我build议您阅读以下文章:

  • 命名函数expression式被揭秘

我build议对方队员之间进行充分的对决,以解决这些争论。

更严重的是,最终它并不重要。 第一种forms(非命名函数)往往会在function较大的情况下变得笨拙,但对于小的(1-2行)函数来说则不算什么。 第二种forms同样是无害的。

任何反对任何风格的论点都是纯粹的自杀 ,imo。

我认为这样的代码唯一的区别是,第二块代码可以重新调用相同的函数(有时使用“定时器函数”是有用的):

 (function(){ function invokeMe() { if(..) setTimeout(invokeMe, 5); } setTimeout(invokeMe, 5); })(); 

内联函数避免名称空间污染,预定义的函数具有更高的重用性。 我认为你可以提出一个合适的例子。

我们不能只是相处吗?

 (function(){ setTimeout( (function InvokeMe(){ /*some code here*/ }), 5); })(); 

只有一件事真的很重要,国际海事组织,这是很容易debugging。 很多步骤跟踪器不能告诉你任何关于func的事情,除了它是匿名的并且有参数,但是你仍然可以通过将定义放在parens中来强制评估来在内联中定义一个名称。 对于非常简单或明显的破坏函数,我想这不是什么大不了的事,但对我来说就像半决赛。 我真的不在乎别人怎么做,如果不会造成痛苦,但我总是试着命名我的funcs,因为它不难,这可能是一个优势。

我知道,这是一个古老的问题,但对我来说,还有一个比已经提到的更重要的区别。 提升每个function都必须创build,因此在内存中保留一些空间,最终必须在GC之后。

命名函数被挂起到周围函数的开头,因此在每个函数调用时创build它们,不pipe它们是否被使用。 匿名函数只有在定义它们的代码被执行时才会被创build。

 //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) } } 

所以,作为一个经验法则:

  • 在循环内部不应该有匿名函数或lambdaexpression式

  • 如果你只需要一个(很less是真的)条件的函数,那么你应该比匿名函数更喜欢匿名函数,因为它们只在需要的时候被创build

  • 如果你知道你的商业(JavaScript),你知道什么时候忽略这个build议

预定义的命名函数可以减lessJavaScriptcallback地狱问题,这是在http://callbackhell.com/上提到的;

没有技术上的原因可以select一个版本。 对我来说通常取决于两件事情:

  1. 我想在另一个上下文中重新使用传入的callback。 在这种情况下,我定义了函数standalone并通过引用。
  2. callback大于〜10行代码,函数在callback之后需要额外的参数。 在这种情况下,很难重build,哪些值实际传递给函数。

例:

 setTimeout(function() { // I need to scroll to see the other arguments // many lines of code }, 0); // <- where does this '0' belong to? 

我更喜欢使用命名函数。 在所有的debugging器(空气,萤火虫,IE)上命名的function名称显示。

例:

注意你也可以有内联的命名函数

 { method: function obj_method(){} } 

这样,当你看堆栈跟踪,你会看到函数obj_method而不是匿名。

你在问什么时候插入一个函数而不是声明它? 在代码中有意义时。 如果你需要从两个不同的地方,它不能内联。 有时候内联会让代码更容易阅读,有时更难。

我倾向于命名function。 匿名函数引用很快,但只能用于简单的东西。 我的经验法则是,如果这个函数超过2行代码,它可能属于它自己的定义。

使用匿名函数的大多数示例代码都会变得很复杂。 但是样本通常很简单。 当事情变得更加复杂的时候,这种方法就会崩溃 我已经看到函数引用嵌套在函数引用中,因为开发者意识到在后续步骤中需要更多的callback函数。 而不是这种基于树的逻辑,我更喜欢组织孤立的function。

而且通常最后很高兴我可以重用我后来定义的函数之一。

匿名函数的一个重要用途是当你需要传递有限范围的数据到你的函数调用中,但是我通常只是把我的函数包装到匿名函数中。

如果你进入testing驱动开发,命名函数也是绝对必要的。

在提供的示例中,函数的声明和使用非常接近,我认为唯一的区别是可读性。 我更喜欢第二个例子。