首先我们来复习一下数组原型上这些会对数组进行遍历的操作:
方法 | 返回值 | 作用 |
---|
forEach | 无返回值 | 遍历数组,对每个元素执行回调函数 |
map | 新数组 | 遍历数组,对每个元素执行回调函数并返回新的元素,组成新的数组 |
filter | 新数组 | 遍历数组,筛选出满足条件的元素,组成新的数组 |
every | 布尔值 | 遍历数组,判断是否所有元素都满足条件 |
some | 布尔值 | 遍历数组,判断是否有至少一个元素满足条件 |
reduce | 累积值 | 遍历数组,对累积值和当前元素执行回调函数,返回最终的累积值 |
他们都接受一个回调函数和一个可选值作为参数:
forEach、map、filter、every、some这五种方法的回调函数都接受三个参数,分别是:
- 当前元素(element):数组中正在处理的当前元素。
- 当前索引(index):数组中正在处理的当前元素的索引。
- 数组(array):调用了该方法的数组。
可选值是 thisArg
,执行 callbackFn
时用作 this
的值,如果没有传入,则回调函数中的 this
值为 undefined
。
reduce方法的回调函数接受四个参数,分别是:
- 累积值(accumulator):累积器累积回调函数的返回值;它是上一次调用回调函数时返回的累积值,或者初始值(如果提供了的话)。
- 当前元素(currentValue):数组中正在处理的当前元素。
- 当前索引(currentIndex):数组中正在处理的当前元素的索引。
- 数组(array):调用了reduce方法的数组。
可选值是 initialValue
,第一次调用回调时初始化 accumulator
的值。如果指定了 initialValue
,则 callbackFn
从数组中的第一个值作为 currentValue
开始执行。如果没有指定 initialValue
,则 accumulator
初始化为数组中的第一个值,并且 callbackFn
从数组中的第二个值作为 currentValue
开始执行。在这种情况下,如果数组为空(没有第一个值可以作为 accumulator
返回),则会抛出错误。
好了,大致上的功能和使用方式就是这样,接下来让我们动手实现他们吧:
forEach
map
filter
every
some
reduce
思考
其实只要理解了 forEach
的实现,再往后推导实现其他的方法就不难了。值得注意的是,我们在判断数组中第i
位是否存在的时候使用了 Object.prototype.hasOwnProperty.call(this, i)
,这是为什么呢?这是为了避免数组中的空位带来影响:
如果数组中某一位的元素正好是 undefined
,那么如果直接通过 this[i]
读取他我们是无法判断这一位到底是空位,还是值为 undefined
的元素。举个例子:
而如果我们在 myMap
中不做 if(Object.prototype.hasOwnProperty.call(this, i))
的判断:
可以发现二者对于空位的返回结果是不同的,因为后者没有跳过对 undefined
元素的处理,导致 undefined
值乘以2,得到 NaN
值。原生的方法中,对空位是会跳过处理的,而对值为 undefined
的元素不会,在我们手写实现的时候要注意到这一点。