在JavaScript事件代码中使用匿名函数而不是命名函数进行回调和参数有什么好处?

Dou*_*hen 44 javascript coding-style callback anonymous-function code-readability

我是JavaScript的新手.我理解这个语言的许多概念,我一直在阅读原型继承模型,而且我正在用越来越多的交互式前端东西吹口哨.这是一种有趣的语言,但我总是被回调意大利面关闭,这是很多非平凡互动模型的典型特征.

对我来说一直以来都很奇怪的是,尽管可读性噩梦是JavaScript嵌套回调的嵌套,但在很多示例和教程中我很少看到的一件事是使用预定义的命名函数作为回调参数.白天我是一名Java程序员,并且放弃了关于企业名称的陈规定型单元代码单元我喜欢用一种具有强大功能的IDE选择的语言来享受的是使用有意义的,如果长,名称可以使代码的意图和含义更加清晰,而不会使实际生产变得更加困难.那么为什么不在编写JavaScript代码时使用相同的方法呢?

考虑到这一点,我可以提出支持和反对这一观点的论据,但我对该语言的天真和新颖性使我无法得出任何结论,为什么这在技术层面上是好的.

优点:

  • 灵活性.具有回调参数的异步函数可以通过许多不同的代码路径之一到达,并且可能不得不编写命名函数来考虑每个可能的边缘情况.
  • 速度.它对黑客心态起着重要作用.把东西固定在它上面直到它起作用.
  • 其他人都在这样做
  • 较小的文件大小,即使非常简单,但每一点都在网络上.
  • 更简单的AST?我假设匿名函数是在运行时生成的,因此JIT不会将名称映射到指令,但我只是在猜测.
  • 更快的调度?也不确定这个.再猜一次.

缺点:

  • 这是可怕的,不可读的
  • 当你在一系列回调中深深嵌套时,它会增加混乱(公平地说,这可能意味着你开始编写构造不良的代码,但这很常见).
  • 对于没有功能背景的人来说,这可能是一个奇怪的概念

有这么多现代浏览器显示出比以前更快地执行JavaScript代码的能力,我没有看到使用匿名回调可能获得的任何微不足道的性能获得是必要的.看来,如果您处于使用命名函数是可行的情况(可预测的行为和执行路径),那么就没有理由不这样做.

那么有什么技术原因或陷阱我不知道这使得这种做法如此普遍吗?

jfr*_*d00 45

我使用匿名函数有三个原因:

  1. 如果不需要名称,因为该函数只在一个地方被调用,那么为什么要为你所在的命名空间添加一个名称.
  2. 匿名函数声明为内联函数,内联函数的优点在于它们可以访问父作用域中的变量.是的,您可以在匿名函数上添加一个名称,但如果它是内联声明的话通常没有意义.所以内联有一个显着的优势,如果你在内联,没有理由在上面写一个名字.
  3. 当在调用它们的代码中定义处理程序时,代码似乎更加自包含且可读.您可以以几乎顺序的方式阅读代码,而不必查找具有该名称的函数.

我确实试图避免匿名函数的深度嵌套,因为理解和阅读可能很麻烦.通常当发生这种情况时,有一种更好的方法来构造代码(有时使用循环,有时使用数据表等等),并且命名函数通常也不是解决方案.

我想我会补充一点,如果一个回调开始超过15-20行,并且不需要直接访问父作用域中的变量,我会试着给它起一个名字并将其分解为它在其他地方声明了自己的命名函数.这里肯定有一个可读性点,如果它放在自己的命名单元中,那么变长的非平凡函数就更容易维护.但是,我最终得到的大多数回调都没那么久,我发现它们更具可读性以保持内联.

  • 1. 这个论点似乎可以扩展到包含我们命名的所有事物。2. 这个论点似乎有点循环,但我想我明白你在说什么。3. 目标是为函数提供非常清晰、有意义的名称,这样您就不必去找它们了。 (3认同)

And*_*ley 10

我自己更喜欢命名函数,但对我来说,它归结为一个问题:

我会在其他任何地方使用此功能吗?

如果答案是肯定的,我会命名/定义它.如果没有,请将其作为匿名函数传递.

如果您只使用一次,那么使用它来拥挤全局命名空间是没有意义的.在当今复杂的前端,这本来是匿名的命名函数的数量增长很快(很容易超过1000对真正复杂的设计),导致(相对)由宁愿匿名函数大的性能提升.

但是,代码可维护性也非常重要.每种情况都不同.如果你不是开始编写很多这些函数,那么无论如何都不会有任何伤害.这完全取决于您的偏好.

关于名字的另一个说明 养成定义长名称的习惯会严重损害文件大小.以下面的例子为例.

假设这两个函数都做同样的事情:

function addTimes(time1, time2)
{
    // return time1 + time2;
}

function addTwoTimesIn24HourFormat(time1, time2)
{
    // return time1 + time2;
}
Run Code Online (Sandbox Code Playgroud)

第二个告诉你它在名称中的确切含义.第一个是更模糊的.但是,名称中有17个字符的差异.假设该函数在整个代码中被调用了8次,这是您的代码不需要的153个额外字节.不是很大,但如果这是一种习惯,将其推断为10s甚至100s的函数很容易意味着下载中有几KB的差异.

但是,需要再次权衡可维护性与性能的好处.这是处理脚本语言的痛苦.

  • 如果要最小化用于部署的javascript,则关于名称大小的点不再相关.在这种情况下选择可读性. (7认同)
  • 仅当您的函数/变量在闭包内定义时(它们应该是)。任何看起来对 minifyer 来说可能是全局的都将被保留,名称的长度仍然很重要。 (2认同)

Ric*_*ckS 8

聚会有点晚了,但一些尚未提到的功能方面,匿名或其他方式......

匿名函数在团队中关于代码的类人对话中不容易被提及。例如,“Joe,你能解释一下算法在那个函数中做了什么。......哪个?fooApp 函数中的第 17 个匿名函数......不,不是那个!第 17 个!”

Anon funcs 对于调试器也是匿名的。(废话!)因此,调试器堆栈跟踪通常只会显示一个问号或类似的东西,当您设置多个断点时,它会变得不那么有用。您遇到了断点,但发现自己向上/向下滚动调试窗口以找出您在程序中的位置,因为嘿,问号功能就是不这样做!

对污染全局命名空间的担忧是有效的,但可以通过将函数命名为您自己的根对象中的节点来轻松解决,例如“myFooApp.happyFunc = function ( ... ) { ... }; ”。

在全局命名空间中可用的函数,或者像上面一样作为根对象中的节点,可以在开发和调试期间直接从调试器调用。例如,在控制台命令行,执行“myFooApp.happyFunc(42)”。这是一种极其强大的能力,在编译的编程语言中(原生)是不存在的。用 anon func 试试。

通过将匿名函数分配给 var,然后将 var 作为回调(而不是内联)传递,可以使它们更具可读性。例如: var funky = function ( ... ) { ... }; jQuery('#otis').click(funky);

使用上述方法,您可以潜在地将几个匿名函数分组在父函数的顶部,然后在其下方,顺序语句的内容变得更加紧密,并且更易于阅读。