第一次见到reduce 是在js 的高级程序设计中,它的意思是把一个数组减少为一个数,举的例子是数组中元素的求和。它接受一个函数作为参数,函数又有两个参数,一个是prev, 前一个值,一个是next, 后一个值,然后函数体就是返回相加的值。
let array = [1, 3, 5]; let sum = array.reduce((prev, next) => { return prev + next; }) console.log(sum);
let sum = 0;
for (let index = 0; index < array.length; index++) { const element = array[index]; sum = sum + element; }
完全没有问题,可以求出数组中的元素的和。数组元素的求和应该都是这样的步骤,我们尝试把这个步骤封装一下, 先直接定义一个函数把所有步骤包含起来, 只把console.log(sum) 变成return sum;
function reduce () { let sum = 0; for (let index = 0; index < array.length; index++) { const element = array[index]; sum = sum + element; } return sum; }
现在来看封装的函数,可以发现有几个问题:
1,let sum = 0 不太合适,在函数中固定一个变量的值,不具有灵活性。这个很简单,可以声明一个变量,让sum 等于传递进行的值。如果没有,可以默为0 , 变量为initValue;
function reduce (array, fn, initValue) { let sum ; initValue ? (sum =initValue): (sum = 0); // 然后循环遍历数组的每一项,和sum 进行相加 for (let index = 0; index < array.length; index++) { const element = array[index]; sum = fn(sum, element); } return sum; }
调用我们自己封装的reduce 时行数组的求和
let result = reduce(array, (sum, element) => sum + element);
现在我们来对比原生的调用方式和自己封装的方式?可以发现没有本质不同。唯一的不同可能是我们的第一个参数是数组,而原生没有,这是因为原生的方法是定义在数组原型上,所 以没有接受数组作为参数,对于理解reduce 函数来说,这没有什么本质的不同。通过封装的过程,我们更能明白接受的函数的参数的意思。这个函数至少要接受两个参数,第一个参数的真正意义应该是调用函数所返回的值,由于在第一个调用函数之前,没有返回值,所以我们可以给它赋初值,或通过第三个参数,或直接调为0。 第二个参数,就是数组的每一项,只有不停的遍历数组中的每一项,最终才能把数组变成一个值。其实初值还有一个更好的办法,如果没有传递进来,可以取数组的第一项作为初始值。最终的函数可能如下:
const reduce = (array, fn, initialValue) => { let sum; if (initialValue) { sum = initialValue; for (const value of array) // 这里用了es6 在for of sum = fn(sum, value) } else { sum = array[0]; for (let i = 1; i < array.length; i++) sum = fn(sum, array[i]) } return sum; }
通过以上分析,我们可以看到,reduce函数真正的核心在于传递进去的函数。正确的使用reduce 就是要正确的写好这个函数,通常这个函数要满足一下,几点要求。
1, 这个函数至少要接受两个参数进行计算,一个参数是累计值,一个参数是数组的每一个元素
2, 这个函数必须要有返回值,因为要用它进行下一步的运算,并且,必须返回一个由参数进行计算的得到结果值,最后返回的值,还是要和数组中的元素类型相同,这还是因为它要进行下一步的运算。
3, 初始的结果值,可以由第三个参数进行传递,也可以不传,如果不传的话,初始的累计值默认为第一个参数。