JS数组reduce方法初识
前言:
从ES5到ES6之后有加了许多数组方法,本文来彻底了解一下reduce方法以及它的执行原理
reduce
具体了解一下reduce这个方法是用来干什么的,官方给出的解释也是用来求和的高阶函数
看一下它的语法
scss
reduce(callbackFn) //没有初始值
reduce(callbackFn, initialValue) //设定初始值
来说明一reduce这几个参数的作用
js
const initialValue = 0;
arr.reduce((acc, cur, curIndex, array)=>{
},initialValue)
acc : 通常被称为累加器
, 上一次调用 callbackFn
的结果。在第一次调用时,如果指定了 initialValue
则为指定的值,否则为 array[0]
的值。
cur : 当前元素的值。在第一次调用时,如果指定了 initialValue
,则为 array[0]
的值,否则为 array[1]
。
curIndex : currentValue
在数组中的索引位置。在第一次调用时,如果指定了 initialValue
则为 0
,否则为 1
。
array : 调用了 reduce()
的数组本身,这句话是官方给出的,稍有一些绕,说白了就是调用reduce方法的该数组
js
//数组求和
const sum = arr.reduce((acc, cur, curIndex, array) => {
console.log(acc, cur, curIndex, array, 'acc, cur, curIndex, array');
return acc + cur
},)
//计算数组中每个元素出现的次数
const names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
let countedNames = names.reduce(function (allNames, name) {
console.log(allNames, 'allNames');
if (name in allNames) {
allNames[name]++;
}
else {
allNames[name] = 1;
}
return allNames;
}, {});
console.log(countedNames, 'countedNames');
异常:
若未提供initialValue
则会抛出错误异常,如下图错误
reduce有无初始值运行机制:
无初始值的 reduce()
代码:
js
const array = [15, 16, 17, 18, 19];
function reducer(accumulator, currentValue, index) {
const returns = accumulator + currentValue;
console.log(
`accumulator: ${accumulator}, currentValue: ${currentValue}, index: ${index}, returns: ${returns}`,
);
return returns;
}
array.reduce(reducer);
回调函数会被调用四次,每次调用的参数和返回值如下表:
accumulator |
currentValue |
index |
返回值 | |
---|---|---|---|---|
第一次调用 | 15 |
16 |
1 |
31 |
第二次调用 | 31 |
17 |
2 |
48 |
第三次调用 | 48 |
18 |
3 |
66 |
第四次调用 | 66 |
19 |
4 |
85 |
array
参数在整个过程中始终不会改变------它始终是 [15, 16, 17, 18, 19]
。reduce()
返回的值将是最后一次回调返回值(85
)。
有初始值的 reduce()
代码:
ini
[15, 16, 17, 18, 19].reduce(
(accumulator, currentValue) => accumulator + currentValue,
10,
);
回调函数会被调用五次,每次调用的参数和返回值如下表:
accumulator |
currentValue |
index |
返回值 | |
---|---|---|---|---|
第一次调用 | 10 |
15 |
0 |
25 |
第二次调用 | 25 |
16 |
1 |
41 |
第三次调用 | 41 |
17 |
2 |
58 |
第四次调用 | 58 |
18 |
3 |
76 |
第五次调用 | 76 |
19 |
4 |
95 |
这种情况下 reduce()
返回的值是 95
。
reduce
是js的高阶函数,它的功能很强大,大家可以根据以下例子来自行深究在项目中的高级用法
求对象数组中值的总和
为了对包含在对象数组中的值进行求和,必须 提供一个 initialValue
,以便每个项都通过回调函数处理
js
const objects = [{ x: 1 }, { x: 2 }, { x: 3 }];
const sum = objects.reduce(
(accumulator, currentValue) => accumulator + currentValue.x,
0,
);
console.log(sum); // 6
展平嵌套数组
js
const flattened = [
[0, 1],
[2, 3],
[4, 5],
].reduce((accumulator, currentValue) => accumulator.concat(currentValue), []);
// flattened 的值是 [0, 1, 2, 3, 4, 5]
统计对象中值的出现次数
js
const names = ["Alice", "Bob", "Tiff", "Bruce", "Alice"];
const countedNames = names.reduce((allNames, name) => {
const currCount = allNames[name] ?? 0;
return {
...allNames,
[name]: currCount + 1,
};
}, {});
// countedNames 的值是:
// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }
按属性对对象进行分组
js
const people = [
{ name: "Alice", age: 21 },
{ name: "Max", age: 20 },
{ name: "Jane", age: 20 },
];
function groupBy(objectArray, property) {
return objectArray.reduce((acc, obj) => {
const key = obj[property];
const curGroup = acc[key] ?? [];
return { ...acc, [key]: [...curGroup, obj] };
}, {});
}
const groupedPeople = groupBy(people, "age");
console.log(groupedPeople);
// {
// 20: [
// { name: 'Max', age: 20 },
// { name: 'Jane', age: 20 }
// ],
// 21: [{ name: 'Alice', age: 21 }]
// }
使用展开语法和 initialValue 连接包含在对象数组中的数组
js
// friends------一个对象数组,其中对象字段"books"是最喜欢的书的列表
const friends = [
{
name: "Anna",
books: ["Bible", "Harry Potter"],
age: 21,
},
{
name: "Bob",
books: ["War and peace", "Romeo and Juliet"],
age: 26,
},
{
name: "Alice",
books: ["The Lord of the Rings", "The Shining"],
age: 18,
},
];
// allbooks------列表,其中包含所有朋友的书籍和 initialValue 中包含的附加列表
const allbooks = friends.reduce(
(accumulator, currentValue) => [...accumulator, ...currentValue.books],
["Alphabet"],
);
console.log(allbooks);
// [
// 'Alphabet', 'Bible', 'Harry Potter', 'War and peace',
// 'Romeo and Juliet', 'The Lord of the Rings',
// 'The Shining'
// ]
数组去重
js
const myArray = ["a", "b", "a", "b", "c", "e", "e", "c", "d", "d", "d", "d"];
const myArrayWithNoDuplicates = myArray.reduce((accumulator, currentValue) => {
if (!accumulator.includes(currentValue)) {
return [...accumulator, currentValue];
}
return accumulator;
}, []);
console.log(myArrayWithNoDuplicates);
使用reduce替换filter和map
个人认为这个用法会比较多一点,大家平时也可以自己多尝试
使用 filter()
和 map()
会遍历数组两次,但是你可以使用 reduce()
只遍历一次并实现相同的效果,从而更高效。(如果你喜欢使用 for
循环,你可以在遍历一次时使用 forEach()
进行过滤和映射。)
js
const numbers = [-5, 6, 2, 0];
const doubledPositiveNumbers = numbers.reduce((accumulator, currentValue) => {
if (currentValue > 0) {
const doubled = currentValue * 2;
return [...accumulator, doubled];
}
return accumulator;
}, []);
console.log(doubledPositiveNumbers); // [12, 4]
按顺序运行Promise
js
/**
* 链接一系列 Promise 处理程序。
*
* @param {array} arr------一个 Promise 处理程序列表,每个处理程序接收前一个处理程序解决的结果并返回另一个 Promise。
* @param {*} input------开始调用 Promise 链的初始值
* @return {Object}------由一系列 Promise 链接而成的 Promise
*/
function runPromiseInSequence(arr, input) {
return arr.reduce(
(promiseChain, currentFunction) => promiseChain.then(currentFunction),
Promise.resolve(input),
);
}
// Promise 函数 1
function p1(a) {
return new Promise((resolve, reject) => {
resolve(a * 5);
});
}
// Promise 函数 2
function p2(a) {
return new Promise((resolve, reject) => {
resolve(a * 2);
});
}
// 函数 3------将由 `.then()` 包装在已解决的 Promise 中
function f3(a) {
return a * 3;
}
// Promise 函数 4
function p4(a) {
return new Promise((resolve, reject) => {
resolve(a * 4);
});
}
const promiseArr = [p1, p2, f3, p4];
runPromiseInSequence(promiseArr, 10).then(console.log); // 1200
使用函数组合实现管道
js
// 组合使用的构建块
const double = (x) => 2 * x;
const triple = (x) => 3 * x;
const quadruple = (x) => 4 * x;
// 函数组合,实现管道功能
const pipe =
(...functions) =>
(initialValue) =>
functions.reduce((acc, fn) => fn(acc), initialValue);
// 组合的函数,实现特定值的乘法
const multiply6 = pipe(double, triple);
const multiply9 = pipe(triple, triple);
const multiply16 = pipe(quadruple, quadruple);
const multiply24 = pipe(double, triple, quadruple);
// 用例
multiply6(6); // 36
multiply9(9); // 81
multiply16(16); // 256
multiply24(10); // 240
稀疏数组中使用reduce
reduce()
会跳过稀疏数组中缺失的元素,但不会跳过 undefined
值。
js
console.log([1, 2, , 4].reduce((a, b) => a + b)); // 7
console.log([1, 2, undefined, 4].reduce((a, b) => a + b)); // NaN
在非数组对象上调用reduce
reduce()
方法读取 this
的 length
属性,然后访问每个整数索引。
js
const arrayLike = {
length: 3,
0: 2,
1: 3,
2: 4,
};
console.log(Array.prototype.reduce.call(arrayLike, (x, y) => x + y));
// 9
结语:以上是我结合和MDN官方文档的demo做出的总结,也是作为自己对reduce的一个深度理解,后续也会持续更新其他知识点