function reduce(collection, iteratee, accumulator) {
var func = isArray(collection) ? arrayReduce : baseReduce,
initAccum = arguments.length < 3;
return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEach);
}
function arrayReduce(array, iteratee, accumulator, initAccum) {
var index = -1,
length = array == null ? 0 : array.length;
if (initAccum && length) {
accumulator = array[++index];
}
while (++index < length) {
accumulator = iteratee(accumulator, array[index], index, array);
}
return accumulator;
}
function baseReduce(collection, iteratee, accumulator, initAccum, eachFunc) {
eachFunc(collection, function(value, index, collection) {
accumulator = initAccum
? (initAccum = false, value)
: iteratee(accumulator, value, index, collection);
});
return accumulator;
}
1. 函数调用开销
-
Lodash: 每次迭代都会调用
iteratee
函数,涉及额外的函数调用堆栈操作。例如:arrayReduce(collection, iteratee, accumulator, initAccum, baseEach);
每个元素处理都需通过
getIteratee
生成的函数,增加了闭包或绑定的开销。 -
for 循环: 直接内联代码,无函数调用开销,所有逻辑在单一作用域内完成。
2. 类型检查与兼容性处理
-
Lodash: 需要处理多种数据类型(数组、对象、类数组等),需调用
isArray(collection)
做类型检查,并动态选择arrayReduce
或baseReduce
。 -
for 循环: 开发者通常已知数据类型(如明确是数组),直接通过
for (let i=0; i<arr.length; i++)
访问,无需类型判断。
3. 参数处理与初始化逻辑
-
Lodash: 需处理
accumulator
的初始化(如initAccum = arguments.length < 3
),可能复制或修改参数。 -
for 循环: 初始值直接通过变量赋值(如
let sum = 0
),无需动态判断参数是否存在。
4. 迭代器通用性代价
-
Lodash: 支持迭代对象(
{ 'a': 1, 'b': 2 }
)和数组,需通过baseEach
处理键值遍历,通用性带来性能损耗。 -
for 循环: 仅针对数组或明确结构优化,可直接通过索引访问元素。
5. 引擎优化限制
-
Lodash: 函数式抽象(如高阶函数
iteratee
)可能阻碍 JS 引擎的优化(如内联、隐藏类优化)。 -
for 循环: 代码模式简单,引擎可轻松应用底层优化(如预分配内存、循环展开)。
总结
原生 for
循环通过减少抽象层、规避函数调用和类型检查,更贴近底层实现,因此在性能敏感场景(如大规模数据遍历)中优势显著。而 Lodash 的 reduce
在通用性和代码可维护性上更优,适合复杂业务逻辑。