如何更有效地做到这一点?
Posted
技术标签:
【中文标题】如何更有效地做到这一点?【英文标题】:How to do it more efficiently? 【发布时间】:2011-02-27 15:21:08 【问题描述】:假设我们应该得到一些数据......
var data = [];
//some code omitted that might fill in the data (though might not)
然后我们需要对数据做一些事情。我的问题是如何更有效地做到这一点。 像这样?:
if (data.length)
for (var i = 0; i < data.length; i++)
//iterate over the data and do something to it
还是就这样?:
for (var i = 0; i < data.length; i++)
//iterate over the data and do something to it
关键是在迭代之前是否检查长度?
【问题讨论】:
希望您的代码始终有效,即使它可能更高效。</spellcheck>
;)
第二种方式(标记为就像这样)可能是更好的方式。它更容易阅读。除非这段代码运行很多次,很多次,否则没有必要使用第一个选项。另外,我什至不确定第一个选项是否更快。如果数据数组很少为空,则可能不是。
为了不使用谷歌翻译(如果是说英语的观众):Donald Knuth 说,“我们应该忘记小的效率,比如说大约 97% 的时间:过早的优化是万恶之源”。不过,是的,在俄语中,很高兴在 SO 上获得答案(/cmets)。问候! =)
过早的优化废话,废话,废话。
【参考方案1】:
我认为基于data
的length
执行for
循环不值得检查,因为如果只执行for
循环,它可能不会对性能产生太大影响几次。
但通常首先获取长度比将其设置为 i<data.length
更快,因为它每次都需要访问变量。至于哪种方式循环数据效率最高,不同的浏览器针对不同的循环进行了优化。但是,只有 IE严重 很慢(在以下测试中比其他浏览器慢几个数量级),所以我认为针对其他浏览器进行优化可能不值得。
以下是以下基准测试的结果(最快用 + 表示,最慢用 - 表示):
FF Chrome Safari Opera IE6 IE7 IE8 方法1 +0.163+ 0.221 0.246 0.269 -11.608- -12.214- -7.657- 方法2 0.175 +0.133+ 0.176 +0.147+ 8.474 8.752 3.267 方法3 0.206 0.235 0.276 0.245 8.002 8.539 3.651 方法4 0.198 0.372 0.447 0.390 +6.562+ +7.020+ 2.920 方法5 0.206 0.372 0.445 -0.400- 6.626 7.096 +2.905+ 方法6 0.176 0.167 +0.175+ 0.223 7.029 8.085 3.167 方法7 -0.263- -0.567- -0.449- 0.413 6.925 7.431 3.242方法一:使用“标准”for
循环:
for (var i=0; i<data.length; i++)
var x = data[i]
方法2:使用“标准”for
循环,分配length
,这样就不必每次都访问了:
for (var i=0, len=data.length; i<len; i++)
var x = data[i]
方法 3: 这类似于 jQuery 在$.each()
中使用的方法。注意分配给len
,这样它就不必每次都得到length
。
for (var x=data[0], len=data.length, i=0; i<len; x=data[++i])
方法4:使用while
循环,往前走。 警告: 需要数组中的每个项目评估为true
,即不是false
、0
、null
、undefined
、''
等!
var x, i=0
while (x = data[i++])
方法五:同方法四,只是用for
做同样的事情:
for (var x,i=0; x=data[i++];)
方法6:使用while
向后循环循环:
var i = data.length
while (i--)
var x = data[i]
方法 7: 使用方法 4/方法 5,但不需要评估项目到 true
,替换 x = data[i++]
:
var x, i=0, len=data.length
while ((x=data[i++]) || i<len)
这首先检查data[i++]
的计算结果是否为true
,然后检查它是否是最后一项,以便它可以在IE 中具有类似的性能,而数组中的null
和false
等问题更少。请注意,在这种情况下使用 while
与 for
时没有明显的区别,但我更喜欢 while
,因为我认为它更清晰。
我通常不喜欢优化,除非有特定的长时间运行的任务,因为它通常会以可读性为代价 - 请仅在您有大量数据需要的特定情况下进行加载等:-)
编辑:因为方法 4/5 在 IE 上非常快,所以添加了一个副作用更少的版本。
编辑 2: 重做所有测试,这次没有任何浏览器扩展,而且时间更长。为了完整起见,这里是代码(抱歉让这篇文章这么长:)
function Tmr()
this.tStart = new Date()
Tmr.prototype =
Time: function()
var tDate = new Date()
var tDiff = tDate.getTime() - this.tStart.getTime()
var tDiff = tDiff / 1000.0 // Convert to seconds
return tDiff
function normalfor(data)
for (var i=0; i<data.length; i++)
var x = data[i]
function fasterfor(data)
for (var i=0, len=data.length; i<len; i++)
var x = data[i]
function jqueryfor(data)
for (var x=data[0], len=data.length, i=0; i<len; x=data[++i])
function whileloop(data)
var x, i=0
while (x = data[i++])
function fixedwhileloop(data)
var x, i=0, len=data.length
while ((x=data[i++]) || i<len)
function forwhileloop(data)
for (var x,i=0; x=data[i++];)
function fixedforwhileloop(data)
for (var x,i=0,len=data.length; (x=data[i++])||i<len; )
function whilebackwards(data)
var i = data.length
while (i--)
var x = data[i]
var undefined
var NUMTIMES = 1000000
var data = '$blah blah blah blah blah|'.split('')
function test()
function getfntime(fn)
// Get the rough time required when executing one of the above functions
// to make sure the `for` loop and function call overhead in `run` doesn't
// impact the benchmarks as much
var t = new Tmr()
for (var xx=0; xx<NUMTIMES; xx++)
fn()
return t.Time()
var fntime = getfntime(test)
function run(fn, i)
var t = new Tmr()
for (var xx=0; xx<NUMTIMES; xx++)
fn(data)
alert(i+' '+(t.Time()-fntime))
setTimeout('run(normalfor, "1:normalfor")', 0)
setTimeout('run(fasterfor, "2:fasterfor")', 0)
setTimeout('run(jqueryfor, "3:jqueryfor")', 0)
setTimeout('run(whileloop, "4:whileloop")', 0)
setTimeout('run(forwhileloop, "5:forwhileloop")', 0)
setTimeout('run(whilebackwards, "6:whilebackwards")', 0)
setTimeout('run(fixedwhileloop, "7:fixedwhileloop")', 0)
//setTimeout('run(fixedforwhileloop, "8:fixedforwhileloop")', 0)
【讨论】:
我什至不知道 jQuery 以如此丑陋的方式管理它。 (我相信可能有一些原因。) @David Maricone:来自 jQuery 本身:A special, fast, case for the most common use of each
(据我所知,它仅用于$.each(array, fn)
而不是$('blah').each(fn)
。)我想它看起来像什么并不重要只要它的用户编写的代码干净且相对较快:-)
另外,提到一个额外的方法,可以使用 do...while 循环。虽然我猜没有太大区别。
do...while
循环的问题是代码会至少执行一次,这种情况非常少见。如果您使用它,您需要检查data
的length
(do...while
大致相当于statements; while (condition) statements;
)它稍微快一些,但这只是因为它不执行第一个条件:-)跨度>
不错的比较,但是你确定FF方法4中的0.008
是正确的吗?这与其他方法有很大的不同……【参考方案2】:
我相信第二个版本更符合习惯,不仅在 javascript 中,而且在大多数编程语言中。如果您担心时间,可以将数组的长度保存在一个变量中,并在循环中使用该变量,但我认为没有必要:
var data = [];
var length = data.length;
for (var i = 0; i < length; i++)
//iterate over the data and do something to it
【讨论】:
长度不是方法;这是一个财产。所以我想没有必要将它保存在其他地方。 缓存长度实际上可以提高性能,因为它可以减少每次对相同值的查找。您可以进一步简化如下:for (var i = 0, j = data.length; i < j; i++) ...
【参考方案3】:
需要检查data.length并迭代它吗?
只需使用 for 循环即可。
【讨论】:
【参考方案4】:我通常不会在迭代之前检查长度。事实上,for 循环本身会在每次迭代时检查长度。
没有值得一提的性能开销。如果长度 = 0,唯一的额外指令是在内存中声明一个新的 int i。你需要成千上万的人才能感受到一毫秒的影响。
【讨论】:
【参考方案5】:如果 data 没有项目,则 data.lenght 将为 0。因此,您的循环将永远不会触发,因为 i = 0 已经小于数组中的项目数。因此,选项 2 很好。
但是,在执行托管代码时,您可能希望在对该对象调用 .length 之前检查 NULL 对象。
【讨论】:
【参考方案6】:我认为在这种情况下循环data
的最有效方法是:
var pos = data.length;
while (pos--)
// do something to => data[pos]
,尽可能减少查找和声明。 它将向后迭代项目,但这可能并不总是值得关注的。
【讨论】:
“显然,删除用于测试何时结束循环的简单条件会产生很大的不同,而您使用 -i 而不是 i- 的事实也符合某些情况。” - codeutopia.net/blog/2009/04/30/… 我想尽可能使用前缀变体,但恐怕--pos
在这里不起作用(我会在块中查看错误的索引)。而且我认为在例如 C 语言而不是 Javascript 编程时会很重要(至少我不记得在很久以前测试它时看到任何差异)。以上是关于如何更有效地做到这一点?的主要内容,如果未能解决你的问题,请参考以下文章
我目前正在检查 UIAlertController 是不是存在以确定正在使用的操作系统版本。有没有更有效的方法来做到这一点?