没有局部函数不是微优化吗?
将内部函数移到该函数之外,以便每次调用该函数时都不会创建它是否是一种微优化?
在这种特殊情况下,doMoreStuff
函数仅在 doStuff
内部使用。我应该担心有这样的本地功能吗?
function doStuff() {
var doMoreStuff = function(val) {
// do some stuff
}
// do something
for (var i = 0; i < list.length; i++) {
doMoreStuff(list[i]);
for (var j = 0; j < list[i].children.length; j++) {
doMoreStuff(list[i].children[j]);
}
}
// do some other stuff
}
一个实际的例子是:
function sendDataToServer(data) {
var callback = function(incoming) {
// handle incoming
}
ajaxCall("url", data, callback);
}
Would moving the inner function outside of this one so that its not created everytime the function is called be a micro-optimisation?
In this particular case the doMoreStuff
function is only used inside doStuff
. Should I worry about having local functions like these?
function doStuff() {
var doMoreStuff = function(val) {
// do some stuff
}
// do something
for (var i = 0; i < list.length; i++) {
doMoreStuff(list[i]);
for (var j = 0; j < list[i].children.length; j++) {
doMoreStuff(list[i].children[j]);
}
}
// do some other stuff
}
An actaul example would be say :
function sendDataToServer(data) {
var callback = function(incoming) {
// handle incoming
}
ajaxCall("url", data, callback);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
在普通 PC 上运行的快速“基准”(我知道有很多未解释的变量,所以不要评论明显的变量,但无论如何它都很有趣):
例如,可以通过将增量移动到条件来优化(运行时间减少了大约 100 毫秒,尽管它不会影响有函数创建和无函数创建之间的差异,所以它并不真正相关)
运行 3 次给出:
然后注释掉该函数创建行,运行 3 次给出:
因此,纯粹在 1000,000 个空函数创建上,您会增加大约半秒。即使假设您的原始代码在手持设备上每秒运行 10 次(假设该设备的整体性能是该笔记本电脑的 1/100,这有点夸张 - 它可能更接近 1/10,尽管会提供一个不错的上限) ,这相当于这台计算机上每秒创建 1000 个函数,只需 1/2000 秒。因此,手持设备每秒都会增加 1/2000 秒的处理开销……每秒半毫秒并不算多。
从这个原始测试中,我得出的结论是,在 PC 上这绝对是一种微观优化,如果您正在为较弱的设备进行开发,那么几乎肯定也是如此。
A quick "benchmark" run on an average PC (i know there are lots of unaccounted-for variables, so dont comment on the obvious, but it's interesting in any case):
It could be optimised by moving the increment to the condition for example (brings running time down by about 100 milliseconds, although it doesn't affect the difference between with and without function creation, so it isn't really relevant)
Running 3 times gave:
Then comment out the function creation line, 3 runs gave:
So purely on 1000,000 empty function creations you add about half a second. Even assuming your original code is running 10 times a second on a handheld device (let's say that devices overall performance is 1/100 of this laptop, which is exaggerated - it's probably closer to 1/10, although will provide a nice upper bound), that's equivalent to 1000 function creations/sec on this computer, which happens in 1/2000 of a second. So every second the handheld device is adding overhead of 1/2000 second of processing... half a millisecond every second isn't very much.
From this primitive test I would conclude that on a PC this is definitely a micro-optimisation, and if you're developing for weaker devices, it is almost certainly as well.
不确定这是否属于“微优化”类别。我会说不。
但这取决于您调用
doStuff
的频率。如果您经常调用它,那么一遍又一遍地创建该函数是不必要的,而且肯定会增加开销。如果您不想在全局范围内拥有“辅助函数”,但又避免重新创建它,您可以像这样包装它:
由于返回的函数是一个闭包,因此它可以访问
doMoreStuff
。请注意,外部函数会立即执行 ((function(){...}())
)。或者您创建一个包含对函数的引用的对象:
有关封装、对象创建模式和其他概念的更多信息可以在书中找到 JavaScript 模式。
Not sure if this falls under the category "micro-optimization". I would say no.
But it depends on how often you call
doStuff
. If you call it often, then creating the function over and over again is just unnecessary and will definitely add overhead.If you don't want to have the "helper function" in global scope but avoid recreating it, you can wrap it like so:
As the function which is returned is a closure, it has access to
doMoreStuff
. Note that the outer function is immediately executed ((function(){...}())
).Or you create an object that holds references to the functions:
More information about encapsulation, object creation patterns and other concepts can be found in the book JavaScript Patterns.
最初的问题是在 2011 年提出的。鉴于此后 Node.js 的兴起,我认为值得重新审视这个问题。在服务器环境中,这里或那里的几毫秒可能非常重要。在负载下保持响应与否之间可能存在差异。
虽然内部函数在概念上很好,但它们可能会给 JavaScript 引擎的代码优化器带来问题。下面的例子说明了这一点:
运行代码的命令:
输出:
如您所见,函数 A 和 B 最初以相同的速度运行。然后由于某种原因发生了去优化事件。从那时起,B 几乎慢了一个数量级。
如果您正在编写性能很重要的代码,那么最好避免使用内部函数。
The original question was asked in 2011. Given the rise of Node.js since then, I thought it's worth revisiting the issue. In a server environment, a few milliseconds here and there can matter a lot. It could be difference between remaining responsive under load or not.
While inner functions are nice conceptually, they can pose problems for the JavaScript engine's code optimizer. The following example illustrate this:
The command for running the code:
The output:
As you can see, function A and B ran at the same speed initially. Then for some reason a deoptimization event occurred. From then on B is nearly an order of magnitude slower.
If you're writing code where performance is importantly, it's best to avoid inner functions.
为了获得嵌套函数(外部函数的内部范围内的函数)的最佳速度,我怀疑您应该使用声明,而不是表达式。
该问题询问“局部函数”和优化,但没有指定如何创建局部函数。但它应该如此,因为对于创建“内部函数”的不同技术,问题的答案可能是不同的。
看看@cleong的答案和测试结果,我怀疑只有他的答案是使用最佳技术来创建函数。创建函数有三种方法,@cleong 向我们展示了提供快速执行的方法。这三种技术是:
构造函数用得不多,它需要一个包含函数体文本的字符串。这在反射编程中非常有用,您可以执行“toString()”来获取函数体,修改,然后构造一个新函数。当然,这或多或少从未做过。
使用声明,但主要用于外部函数,而不是内部函数(“内部函数”是指嵌套在另一个函数中的函数)。然而,根据 @cleong 测试,它似乎非常快;与外部函数一样快。
表达式是每个人都使用的。这可能不是最好的主意;但这是每个人都会做的。
函数声明和函数表达式之间的一个主要区别是声明会受到提升。每个人都知道“var”声明被提升;但“函数”声明也是如此。对于提升的事物,在编译时执行计算以确定该事物所需的内存空间。据推测,人们会期望内部函数在编译时进行编译,并且可以像编译后的外部函数一样运行。
我有一本大约六年前弗兰尼根的《权威指南》一书,我记得读过我刚刚在这里写的内容的反面。他说了这样的话:表达式是编译的,而声明不是。虽然他是世界上 JavaScript 的“权威指南”,但我一直怀疑他可能把这本指南搞混了。我怀疑函数内部声明比函数表达式更“准备就绪”。 stackOverflow 页面上的测试结果似乎证实了我长期以来的怀疑。
看看 @cleong 测试结果,如果考虑最佳执行速度,似乎声明而不是表达式才是内部函数的最佳选择。
For optimal speed with a nested function (function within internal scope of an outer function), I suspect you should use declarations, not expressions.
The question asks about "local functions" and optimization, but doesn't specify how the local functions are created. But it should, because the question's answer probably is different for the different techniques by which the "inner function" can be created.
Looking at the answer and test results by @cleong, I suspect that only his answer is using the optimal technique for function creation. There are three ways to create a function, and @cleong is showing us the one that provides fast execution. The three techniques are:
Constructor isn't used much, it requires a string that has the text of the function body. This would be useful in reflective programming, where you do a "toString()" to get the function body, modify, then construct a new function. And that, of course, is more-or-less never done.
Declaration is used, but mostly for outer functions, not inner functions (by "inner function" I mean a function nested within another). Yet, based upon @cleong tests, it seems to be very fast; just as fast as an outer function.
Expressions are what everyone uses. This might not be the best idea; but it's what everyone does.
One major difference between function declarations and function expressions is that the declarations are subject to hoisting. Everyone knows that "var" declarations are hoisted; but so are "function" declarations. For things that are hoisted, computations are performed at compile time to determine the memory space that will be needed for the thing. Presumably, one would expect that the inner function is compiled at compile time, and can run much as would a compiled outer function.
I have a copy of Flannigan's "The Definitive Guide" book from about six years ago, and I remember reading the reverse of what I just wrote here. He said something like: expressions are compiled, and declarations are not. While he is the world's "definitive guide" to JavaScript, I have always suspected he might have gotten this one mixed up and backwards. I suspect that function inner declarations are more "ready to go" than are function expressions. The test results on this stackOverflow page seem to confirm my long held suspicions.
Looking at the @cleong test results, it just seems that declaration, not expression, is the way to go for inner functions, if optimal execution speed is a concern.
这完全取决于函数被调用的频率。如果它是每秒调用 10 次的 OnUpdate 函数,那么这是一个不错的优化。如果每页调用三次,则属于微优化。
虽然嵌套函数定义很方便,但从来不需要(它们可以被函数的额外参数替换)。
嵌套函数的示例:
同样的事情,现在用参数代替:
It completely depends on how often the function is called. If it's a OnUpdate function that is called 10 times per second it is a decent optimalisation. If it's called three times per page, it is a micro optimalisation.
Though handy, nested function definitions are never needed (they can be replaced by extra arguments for the function).
Example with nested function:
Same thing, now with argument instead:
这绝对是一个微观优化。首先使用函数的全部原因是为了让你的代码更干净、更易于维护和更具可读性。函数向代码段添加语义边界。每个函数应该只做一件事,而且应该干净利落地完成。因此,如果您发现您的函数同时执行多项操作,那么您就可以将其重构为多个例程。
仅当某些工作运行速度太慢时才进行优化(如果它尚未工作,则优化还为时过早。期间)。请记住,没有人会为比他们的需要/要求更快的程序支付额外费用...
编辑:考虑到该程序尚未完成,这也是一个过早的优化。为什么这么糟糕?嗯,首先你花时间做一些从长远来看可能不重要的事情。其次,您没有基准来查看您的优化是否在现实意义上改进了任何东西。第三,在运行之前就降低了可维护性和可读性,因此与使用干净简洁的代码相比,运行起来会更困难。第四,你不知道程序中的其他地方是否需要
doMoreStuff
,直到你完成它并了解你的所有需求(也许不太可能,具体取决于具体的细节,但不会超出范围)可能性的领域)。Donald Knuth 说过早的优化是万恶之源是有原因的......
It is absolutely a micro-optimization. The whole reason for having functions in the first place is so that you make your code cleaner, more maintainable and more readable. Functions add a semantic boundary to sections of code. Each function should only do one thing, and it should do it cleanly. So if you find your functions performing multiple things at the same time, you've got a candidate for refactoring it into multiple routines.
Only optimize when you've got something working that's too slow (If it's not working yet, it's too early to optimize. Period). Remember, nobody ever paid extra for a program that was faster than their needs/requirements...
Edit: Considering that the program isn't finished yet, it's also a premature optimization. Why is that bad? Well, first you're spending time working on something that may not matter in the long run. Second, you don't have a baseline to see if your optimizations improved anything in a realistic sense. Third, you're reducing maintainability and readability before you've even got it running, so it'll be harder to get running than if you went with clean concise code. Fourth, you don't know if you'll need
doMoreStuff
somewhere else in the program until you've finished it and understand all your needs (perhaps a longshot depending on the exact details, but not outside the realm of possibility).There's a reason that Donnald Knuth said Premature optimization is the root of all evil...