我试图找到运行具有自己范围的for循环的最快方法。我比较的三种方法是:
var a = "t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t,t".split();
// lodash .each -> 1,294,971 ops/sec
lodash.each(a, function(item) { cb(item); });
// native .forEach -> 398,167 ops/sec
a.forEach(function(item) { cb(item); });
// native for -> 1,140,382 ops/sec
var lambda = function(item) { cb(item); };
for (var ix = 0, len = a.length; ix < len; ix++) {
lambda(a[ix]);
}
这是在OS X上的Chrome 29上。您可以在此处自行运行测试:
lodash&#39; .each
几乎是原生.forEach
的两倍?而且,它如何比普通for
更快?巫术?黑魔法?
答案 0 :(得分:86)
_.each()
与[].forEach()
不完全兼容。请参阅以下示例:
var a = ['a0'];
a[3] = 'a3';
_.each(a, console.log); // runs 4 times
a.forEach(console.log); // runs twice -- that's just how [].forEach() is specified
所以lodash的实现缺少if (... in ...)
检查,这可能解释了性能差异。
如上面的评论所述,与本地for
的区别主要是由测试中的附加函数查找引起的。使用此版本可获得更准确的结果:
for (var ix = 0, len = a.length; ix < len; ix++) {
cb(a[ix]);
}
答案 1 :(得分:22)
http://kitcambridge.be/blog/say-hello-to-lo-dash/
lo-dash开发人员解释(此处和视频)本机forEach
的相对速度因浏览器而异。仅仅因为forEach
是原生的并不意味着它比使用for
或while
构建的简单循环更快。首先,forEach
必须处理更多特殊情况。其次,forEach
使用回调,具有函数调用的(潜在)开销等。
chrome
(至少对于lo-dash开发者而言)具有相对较慢的forEach
。因此,对于该浏览器,lo-dash使用它自己的简单while
循环来获得速度。因此,你看到的速度优势(但其他人没有)。
巧妙地选择原生方法 - 仅使用原生方法 如果已知在给定环境中快速执行 - Lo-Dash避免了相关的性能成本和一致性问题 与当地人一起。
答案 2 :(得分:16)
是的,lodash /下划线甚至没有与.forEach
相同的语义。除非引擎能够快速检查没有getter的稀疏数组,否则有一个微妙的细节会使函数真正变慢。
这符合99%规范,runs at the same speed as lodash each in V8适用于常见情况:
function FastAlmostSpecForEach( fn, ctx ) {
"use strict";
if( arguments.length > 1 ) return slowCaseForEach();
if( typeof this !== "object" ) return slowCaseForEach();
if( this === null ) throw new Error("this is null or not defined");
if( typeof fn !== "function" ) throw new Error("is not a function");
var len = this.length;
if( ( len >>> 0 ) !== len ) return slowCaseForEach();
for( var i = 0; i < len; ++i ) {
var item = this[i];
//Semantics are not exactly the same,
//Fully spec compliant will not invoke getters
//but this will.. however that is an insane edge case
if( item === void 0 && !(i in this) ) {
continue;
}
fn( item, i, this );
}
}
Array.prototype.fastSpecForEach = FastAlmostSpecForEach;
首先检查未定义,我们不会在循环中惩罚正常数组。引擎可以使用其内部来检测奇怪的数组,但V8不会。
答案 3 :(得分:3)
这是一个更新的链接(大约2015年),显示了比较所有三个for(...)
,Array.forEach
和_.each
的效果差异:
https://jsperf.com/native-vs-underscore-vs-lodash
注意:放在这里,因为我还没有足够的分数来评论接受的答案。