本篇内容主要讲解“reduce lodash.reduce实现原理是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“reduce lodash.reduce实现原理是什么”吧!
基本实现
实现思路:
判断是否有初始值,因为有初始值和没有初始值对回调函数(reducer)执行的次数是有影响的。
遍历数组
组合参数传递给 reducer 进行执行
获取到第三步返回值的时候,要把返回值存储起来,在下一次便利的时候作为reducer第一个参数来替换初始值。
返回最终计算的value值
function reduce(array, reducer, initialValue = null) { let value = initialValue === null ? array[0] : initialValue; // 思路1 let startIndex = initialValue === null ? 1 : 0; // 思路1 for(let i = startIndex; i < array.length; i++) { // 思路 2 const item = array[i] const res = reducer(value, item, i) // 思路3 value = res; // 思路4 } return value; // 思路5}
测试一下:
console.log(reduce([1,2,3], (a, b) => (a + b), 0)) // 6console.log(reduce([1,2,3], (a, b) => (a + b))) // 6
看起来是不是挺简单的,代码其实还可以更简洁一点:
function reduce(array, reducer, value = null) { value = value === null ? array[0] : value; for(let i = null ? 1 : 0; i < array.length; i++) { value = reducer(value, array[i], i); } return value;}
lodash 中的 reduce 实现有何不同?
lodash中 的 reduce 不仅可以对数组生效,也可以对普通 object 、类数组对象生效。
不过也针对数组其实单独实现了一个 arrayReduce
函数,不过没有对外。
来看一下 reduce
和 arrayReduce
源码
function reduce(collection, iteratee, accumulator) { const func = Array.isArray(collection) ? arrayReduce : baseReduce const initAccum = arguments.length < 3 return func(collection, iteratee, accumulator, initAccum, baseEach)}function arrayReduce(array, iteratee, accumulator, initAccum) { let index = -1 const 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 reduce(array, reducer, value) { const noInitialValue = arguments.length < 3 // 用参数的数量来判断是否有初始值 let index = -1 // 遍历索引 - 1,因为下面 while 循环前先加了 1 const length = array == null ? 0 : array.length // 判断数组是否存在和缓存数组长度 // 这个if 语句中做了我上面思路1中初始值的问题和遍历次数的问题 if (noInitialValue && length) { // && length 判断了数组是否为空 value = array[++index] // 没有有初始值,则取数组中第一为,注意 index 变成了0,下面 while 循环前会先加 1,因此循环次数会少一次。 } while (++index < length) { value = reducer(value, array[index], index, array) } return value}
可以看出其实大部分逻辑还是和前面的简单实现差不多,不过考虑更全一些,有值得借鉴的地方:
参数判断逻辑更有力,不管外界传递了第三个参数是啥,都说明有初始值
考虑了数组不存在或者为空的情况
下面我们再看一下,去除数组相关的代码来看看针对其他对象类型怎么处理的。
function reduce(collection, iteratee, accumulator) { const func = baseReduce; const initAccum = arguments.length < 3 return func(collection, iteratee, accumulator, initAccum, baseEach)}
其他类型的都会教给 baseReduce
函数去处理。
// baseReducefunction baseReduce(collection, iteratee, accumulator, initAccum, eachFunc) { // 使用外部传递进来的遍历方法进行遍历对象,然后传递了一个 callback 给 eachFunc eachFunc(collection, (value, index, collection) => { // 初始值设置, accumulator = initAccum ? (initAccum = false, value) : iteratee(accumulator, value, index, collection) }) return accumulator}
使用外部传递进来的遍历方法进行遍历对象,然后传递了一个 callback 给 eachFunc,来执行 iteratee (也就是前面说的reducer),callback 内部的代码就和前面 for 循环或者 while 循环的代码类似的,就是组合参数传递给 reducer 进行执行,不过直接看可能有点不好理解中,了解了原理再来看应该可以理解,注意事项:
initAccum 为 false 时,说明有初始值,直接执行 iteratee。
initAccum 为 true,说明没有初始值,需要添加初始值,因此第一次循环就是赋值给初始值,然后把 initAccum 设置为false,没有进行执行 iteratee,比没有初始值少一次执行,符合逻辑。
eachFunc
用的是 reduce
中传递进来的 baseEach
,内部主要就是对对象属性进行遍历的操作,然后把属性值和索引以及对象本身传递给 callback,稍微需要注意的就是可能遇到类数组的对象,为了保证顺序,使用类数组放入索引进行遍历,而其他对象并不能保证属性的传递顺序,可以再看一下baseEach实现的代码:
function baseEach(collection, iteratee) { if (collection == null) { return collection } // 不是类数组则使用 baseForOwn 处理 if (!isArrayLike(collection)) { return baseForOwn(collection, iteratee) } const length = collection.length const iterable = Object(collection) // 使用arguments测试了一下,好像没啥作用 let index = -1 // 遍历类数组 while (++index < length) { if (iteratee(iterable[index], index, iterable) === false) { break } } return collection}
不是 isArrayLike
的对象遍历与本篇文章的内容没有啥关系了,因此就不深入了。
到此,相信大家对“reduce lodash.reduce实现原理是什么”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!