实现Underscore.js的reduce函数:accumulator未定义时如何跳过首次迭代
精简Underscore.js reduce实现的方案
问题分析
你的代码核心逻辑是正确的,但数组和对象的处理模块存在大量重复代码。两者仅在元素获取方式(数组用索引、对象用键)和初始值来源上有差异,其余逻辑完全一致,可通过抽离公共逻辑消除冗余。
优化后的代码
_.reduce = function (collection, iteratee, accumulator, context) { // 统一生成可迭代的键列表:数组用索引序列,对象用Object.keys const keys = Array.isArray(collection) ? Array.from({ length: collection.length }, (_, i) => i) : Object.keys(collection); let returnVal = accumulator; let startIndex = 0; // 处理accumulator未定义的场景:用第一个元素初始化,循环从第二个元素开始 if (returnVal === undefined) { returnVal = collection[keys[0]]; startIndex = 1; } // 统一执行循环逻辑,无需区分数组/对象 for (let i = startIndex; i < keys.length; i++) { const key = keys[i]; returnVal = iteratee.call( context, returnVal, collection[key], key, collection ); } console.log(returnVal); return returnVal; };
优化说明
- 统一键列表:将数组索引转换为和对象键结构一致的列表,让数组和对象的循环逻辑完全复用,避免分支冗余。
- 提前确定循环起点:在循环前就完成accumulator的判断,直接设置初始值和起始索引,解决了你之前用
continue无效的问题。 - 消除重复代码块:原代码中数组和对象的循环、iteratee调用模块完全重复,现在通过统一逻辑实现了代码精简,可读性和维护性大幅提升。
额外改进建议
- 用
let/const替代var:避免变量提升带来的潜在问题,提升代码安全性。 - 处理空集合边界:当集合为空且未传入accumulator时,应抛出错误或返回undefined,对齐Underscore原生行为。
- 移除调试输出:生产代码中无需保留
console.log。
内容的提问来源于stack exchange,提问作者Niels Koop
相关产品推荐
相关产品推荐

