reduce 方法
reduce方法主要是将数组中每个元素(但是不包括被删除以及从未赋值的元素)都执行一遍reducer函数,并将结果通过一个值返回。也就是说,reduce的返回值是单个值。
reduce 方法语法
arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
reducer 函数(callback)
根据MDN,reducer函数接收4个参数:
- 累加器 Accumulator (acc)
- 当前值 Current Value (cur)
- 当前索引 Current Index (idx), 可选
- 源数组 Source Array (src), 可选 reducer函数的返回值被分配给累加器,该返回值会在每次迭代中被记住,最终成为单个返回值。
initialValue
第一次调用callback函数的第一个参数(累加器)的值,如果没有提供初始值,则使用数组中第一个元素的值。如果数组为空,且没有初始值,调用reduce方法会报错,抛出TypeError错误。
返回值
函数累计处理的结果
一些注意点:
1、第一个执行回调函数的时候,acc 和 cur 的取值有两种情况:
- 有初始值:acc取值为默认值,cur取值为数组第一个元素,从索引0开始执行callback
- 无初始值:acc取值为数组第一个元素,cur取值为数组第二个元素,从索引1开始执行callback 2、数组为空的情况:
- 有初始值:不会执行回调函数,返回值为初始值
- 无初始值: 报错,抛出TypeError错误 3、数组长度为1:
- 有初始值:acc取值为默认值,cur取值为数组第一个元素
- 无初始值:不执行回调函数,acc取值为数组第一个元素,返回值为数组第一个元素 4、reduce方法会自动忽略数组中从未赋值的元素,比如:
let arr =[,]
console.log(arr.length); // 1
console.log(arr); // [empty]
let filterArr = [];
// 通过hasOwnProperty方法过滤数据
for(let i = 0; i < arr.length; i++) {
if (Object.prototype.hasOwnProperty(arr, i)) {
filterArr.push(i)
}
}
console.log(filterArr); // []
console.log(filterArr.length); // 0
reduce实现
reduce接收两个参数:
- @param { Function } callback
- @param { Any } 可选的initialValue callback 接收前面介绍的四个参数
- @param acc
- @param cur
- @param idx
- @param array
Array.prototype.myReduce = function(fn, initialValue) {
// 判断调用对象是否为Array
if (Object.prototype.toString.call(this) !== '[object Array]') {
throw new SyntaxError('Unexpected token \'\.\'')
}
// 判断fn是否为函数
if (typeof fn === 'object' && fn !== null) {
throw new TypeError('#<Object> is not a function')
} else if (typeof fn !== 'function') {
throw new TypeError(`${fn} is not a function`)
}
// 过滤数组中从未赋值的元素
// 比如[,].length = 1, 过滤后数组长度为0
// console.log([,]) // [empty]
let filterArr = [];
for(let i = 0; i < this.length; i++) {
if (Object.prototype.hasOwnProperty.call(this, i)) {
filterArr.push(this[i])
}
}
// 数组为空且没有初始值,抛出异常
if (!filterArr.length && arguments.length === 1 ) {
throw new TypeError('Reduce of empty array with no initial value')
}
// 空数组有初始值,返回初始值
if (!filterArr.length && arguments.length > 1 ) {
arguments[1]
return arguments[1]
}
// 数组长度为1,没有初始值,返回数组第一个元素
if (filterArr.length === 1 && arguments.length === 1) {
return filterArr[0]
}
for(let i = 0; i < filterArr.length; i++) {
if (typeof initialValue === 'undefined') {
initialValue = fn(filterArr[i], filterArr[i+1], i+1, filterArr)
++i;
} else {
initialValue = fn(initialValue, filterArr[i], i, filterArr)
}
}
return initialValue
}
let test1 = [1,2,3].reduce((acc, cur) => acc + cur);
let test2 = [1,2,3].myReduce((acc, cur) => acc + cur);
console.log('reduce', test1) // reduce 6
console.log('myReduce', test2) // myReduce 6
let test3 = [].reduce((acc, cur) => acc, null);
let test4 = [].myReduce((acc, cur) => acc + cur, null);
console.log('reduce', test3) // reduce null
console.log('myReduce', test4) // myReduce null
let test5 = [1].reduce((acc, cur) => acc);
let test6 = [1].myReduce((acc, cur) => acc);
console.log('reduce', test5) // reduce 1
console.log('myReduce', test6) // myReduce 1
let test7 = ['1',null,undefined,,3,4].reduce((acc, cur) => acc + cur);
let test8 = ['1',null,undefined,,3,4].myReduce((acc, cur) => acc + cur);
console.log('reduce', test7) // reduce 1nullundefined34
console.log('myReduce', test8) // myReduce 1nullundefined34
let test9 = [].reduce((acc, cur) => acc + cur); // TypeError,后面代码不执行
let test10 = [].myReduce((acc, cur) => acc + cur);
console.log('reduce', test9)
console.log('myReduce', test10)
reduce应用例子
1、求和
let total = [1,2,3,4].reduce((acc, cur) => acc + cur, 0)
2、flatten 扁平化数组
// concat 方法可以接收数组或者值
// ['algh', 'happy'].concat(1, [2, 3]);
// 输出:['algh', 'happy', 1, 2, 3]
const flatten = list =>
list.reduce((acc,cur) =>
acc.concat(Array.isArray(cur) ? flatten(cur) : cur)
,[])
let test11 = flatten([1,2,[3,4,5,[6,[7,8,[9]]]],10,11])
console.log(test11)
3、将一个数组转换为对象
/*
* [
* { name: 'lili' },
* { age: 21 }
* ]
*
* { name: 'lili', age: 21 }
*/
const arrToObj = list =>
list.reduce((acc, cur) => {
// Object.entries(cur): [["name", "lili"]]
// pair: ["name", "lili"]
const [pair] = Object.entries(cur);
const [k, v] = pair;
acc[k] = v;
return acc;
}, {})
const test12 = arrToObj([{name: 'lili'},{age: 20}])
console.log(test12) // {name: 'lili', age: 20}
4、获取对象的值
const getObj = (obj, path) => {
const pathArr = path.split('.');
return pathArr.reduce((acc, cur) => acc ? acc[cur] : null, obj)
}
let test13 = getObj({ b: { c: { d: 2 }}}, 'b.c.d');
console.log(test13) // 2
5、实现map
// 错误的示例
if (!Array.prototype.myMap) {
Array.prototype.myMap = (fn, thisArg) => // 这里用箭头函数,会导致this指向window,应改为普通函数
this.reduce((acc, cur, index, arr) => {
acc[index] = fn.call(thisArg, cur, index, arr)
return acc
},[])
}
// 所以在实现某个原型方法的时候一定要用普通函数,箭头函数的this值绑定会导致不可预估的bug
// 正确的示例
if (!Array.prototype.myMap) {
Array.prototype.myMap = function (fn, thisArg) {
return this.reduce((acc, cur, index, arr) => {
acc[index] = fn.call(thisArg, cur, index, arr)
return acc
},[])
}
}
let test14 = [1,2,3].myMap((item) => item +1 )
console.log(test14)
6、数组去重
const array = [1,2,1,2,3,5,4,5,3,4,4,4,4];
const newArray = array.sort().reduce((acc, cur) => {
if (acc.indexOf(cur) === -1) {
acc.push(cur)
}
return acc;
}, [])
const newArr = Array.from(new Set(array));
console.log(newArray, newArr) // 两个[1,2,3,4,5]