LinDaiDai/niubility-coding-js

💪第6期第3题:reduce方法有初始值和没有初始值的区别?

Opened this issue · 1 comments

reduce函数的第一个参数是一个回调函数,第二个参数为可选的初始值。

如果有初始值的话,回调函数就会从数组的第0项开始执行,也就是会执行arr.length次;

但是如果没有初始值的话,会默认取数组的第0项为初始值,回调函数会从数组的第1项开始执行,也就是会执行arr.length - 1次。

这点从我们手写一个reduce的实现就可以看出来,代码如下:

Array.prototype.MyReduce = function (fn, initialValue) {
  var arr = Array.prototype.slice.call(this);
  var pre, startIndex;
  pre = initialValue ? initialValue : arr[0];
  startIndex = initialValue ? 0 : 1;
  for (var i = startIndex; i < arr.length; i++) {
    pre = fn.call(null, pre, arr[i], i, this)
  }
  return pre
}

过程分析:

  • 首先,map、reduce这种方法都是数组原型对象上的方法,所以我将MyReduce定义在Array.prototype 上,这样你就可以直接使用ary.MyReduce()这样的方式调用它了(ary是一个类似于这样的数组[1, 2, 3])。
  • 对于参数,我们参考原生reduce,它接收的第一个参数是一个回调函数,第二个是初始值
  • var arr = ...的作用是获取调用MyReduce函数的那个变量,也就是说this会指向那个变量,例如ary.MyReduce(),那么此时this就为ary
  • 至于为什么不使用var arr = this;的方式而是使用Array.prototype.slice.call(this),算是实现一个浅拷贝吧,因为reduce是不会改变原数组的。
  • 然后就是定义传入reduce中的回调函数的第一个参数pre,也就是上一次运行结果的返回值,可以看到这里就用到了初始值initialValue,如果存在初始值就取初始值,不存在则默认取数组第0项。(当然这里直接用initialValue ?来判断存不存在并不准确,因为我们知道0也会被判断为false)
  • 接着是定义循环开始的下标startIndex,若是不存在初始值,则初始值是会取数组中的第0项的,相当于第0项并不需要运行,所以startIndex会是1,而如果有初始值的话则需要将数组的每一项都经过fn运行一下。
  • 最后,for循环中使用fn.call()来调用fn函数,并且最后一个参数是要把原来的数组传递到回调函数中,也就是这里的this

"至于为什么不使用var arr = this;的方式而是使用Array.prototype.slice.call(this),算是实现一个浅拷贝吧,因为reduce是不会改变原数组的。"不想影响原数组的话, 那岂不是应该用深拷贝来拷贝数组吗