看看Lo-Dash。作者在研究这些性能问题方面做了大量工作,尤其是在这篇博文和这段视频中。
如果性能是您的目标,您最好编写自己的迭代器,该迭代器只执行您需要的操作,而不执行其他操作——那么它的性能应该非常好。例如,在许多情况下,您只需要数组元素而不需要索引。您绝对不需要使用.call()
将数组元素作为 传递给回调this
,因为它也作为命名参数传递。你真的需要return false;
在回调中终止循环吗?我不记得我上次使用它是什么时候了。所以它可以很简单:
function eachElement( array, callback ) {
for( var i = 0, n = array.length; i < n; ++i ) {
callback( array[i] );
}
}
在速度方面很难超越它。
当你需要索引时,你可以有一个单独的版本:
function eachElementAndIndex( array, callback ) {
for( var i = 0, n = array.length; i < n; ++i ) {
callback( array[i], i );
}
}
您可以使用较短的函数名称;为了清楚起见,我在这里使用了这些长名称。事实证明这eachElementAndIndex()
已经足够快了,所以你可以调用它each()
并完成它。
值得注意的是,正如@YuryTarabanko在上面的评论中指出的那样,这些简化的迭代器不符合Array.prototype.forEach()
. 除了不将数组元素作为this
第三个参数传递并且不将整个数组作为第三个参数传递之外,它们不检查丢失的元素,并且会为每个数组索引调用回调,0
无论array.length - 1
数组元素是否实际存在。例如。
事实上,Underscore.js 的_.each()
在这方面是不一致的。当它回退到 时Array.prototype.forEach()
,它会跳过丢失的元素,但是当它使用自己的for
循环时,它会包含它们。
尝试转到underscorejs.org并将其粘贴到 Chrome 控制台中:
function eachElementAndIndex( array, callback ) {
for( var i = 0, n = array.length; i < n; ++i ) {
callback( array[i], i );
}
}
function log( e, i ) {
console.log( i + ':', e );
}
var array = [];
array[2] = 'two';
console.log( 'eachElementAndIndex():' );
eachElementAndIndex( array, log );
console.log( '_.each() using native .forEach():' );
_.each( array, log );
console.log( '_.each() using its own loop:' );
var saveForEach = Array.prototype.forEach;
delete Array.prototype.forEach;
_.each( array, log );
Array.prototype.forEach = saveForEach;
console.log( 'Done' );
它记录:
eachElementAndIndex():
0: undefined
1: undefined
2: two
_.each() using native .forEach():
2: two
_.each() using its own loop:
0: undefined
1: undefined
2: two
Done
在许多(可能是大多数)情况下,缺少元素并不重要。您正在使用您创建的数组或 JSON 数组,并且您知道它没有任何缺失的元素。特别是对于 JSON 数组,这从来都不是问题,因为 JSON 无法创建缺少元素的数组。JSON 数组可以包含null
元素,但 `.forEach() 包含与其他元素一样的元素。
只要您的简化迭代器执行您想要的操作并且未命名Array.prototype.forEach
,您就不必担心它是否符合您自己的规范之外的任何规范。但是,如果您确实需要跳过丢失的数组元素,请在您自己的代码中考虑到这一点或使用标准的.forEach()
.
此外,只是为了进一步简化您正在查看的下划线代码,因为我们在谈论.forEach()
,这意味着我们只谈论 的Array
部分_.each()
,而不是Object
部分。所以实际感兴趣的代码路径是这样的:
var each = _.each = _.forEach = function(obj, iterator, context) {
if (obj == null) return;
if (obj.length === +obj.length) {
for (var i = 0, l = obj.length; i < l; i++) {
if (iterator.call(context, obj[i], i, obj) === breaker) return;
}
}
};