作为一个前端开发者,一定有接触过 reduce
函数,它是一个强大而实用的数组方法,熟练掌握 reduce
的使用可以在开发中提高开发效率和代码质量。本文介绍的 reduce
的 10 个技巧值得拥有,可以让你少写很多代码!
reduce
方法在数组的每个元素上执行提供的回调函数迭代器。它传入前一个元素计算的返回值,结果是单个值,它是在数组的所有元素上运行迭代器的结果。
迭代器函数逐个遍历数组的元素,在每一步中,迭代器函数将当前数组值添加到上一步的结果中,直到没有更多元素要添加。
语法
参数包含回调函数和可选的初始值,如下:
array.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
callback
(必须):执行数组中每个值(如果没有提供initialValue
则第一个值除外)的reducer
函数,包含四个参数
accumulator
(必须):累计器累计回调的返回值; 它是上一次调用回调时返回的累积值,初始值可以通过initialValue
定义,默认为数组的第一个元素值,累加器将保留上一个操作的值,就像静态变量一样currentValue
(必须):数组中正在处理的元素index
(可选):数组中正在处理的当前元素的索引。 如果提供了initialValue
,则起始索引号为0
,否则从索引1
起始。
注意:如果没有提供
initialValue
,reduce
会从索引1
的地方开始执行callback
方法,跳过第一个索引。如果提供initialValue
,从索引0
开始。
array
(可选):调用reduce()
的数组
initialValue
(可选):作为第一次调用callback
函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用reduce
将报错。
1. 计算数组的最大值和最小值
有很多种方式可以获取数组的最大值或最小值?
使用 Math.max 和 Math.min
使用 Math 的 API 是最简单的方式。
const arrayNumbers = [-1, 10, 6, 5, -3]; const max = Math.max(...arrayNumbers); // 10 const min = Math.min(...arrayNumbers); // -3 console.log(`max=${max}`); // max=10 console.log(`min=${min}`); // min=-3
使用 reduce
一行代码,就可以实现与 Math
的 API 相同的效果。
const arrayNumbers = [-1, 10, 6, 5, -3]; const getMax = (array) => array.reduce((max, num) => (max > num ? max : num)); const getMin = (array) => array.reduce((max, num) => (max < num ? max : num)); const max = getMax(arrayNumbers); // 10 const min = getMin(arrayNumbers); // -3 console.log(`max=${max}`); // max=10 console.log(`min=${min}`); // min=-3
或者写成一个函数:
const arrayNumbers = [-1, 10, 6, 5, -3]; const getMaxOrMin = (array, type = "min") => type === "max" ? array.reduce((max, num) => (max > num ? max : num)) : array.reduce((max, num) => (max < num ? max : num)); const max = getMaxOrMin(arrayNumbers, "max"); // 10 const min = getMaxOrMin(arrayNumbers, "min"); // -3 console.log(`max=${max}`); // max=10 console.log(`min=${min}`); // min=-3
2. 数组求和和累加器
使用 reduce
,可以轻松实现多个数相加或累加的功能。
// 数组求和 const sum = (...nums) => { return nums.reduce((sum, num) => sum + num); }; // 累加器 const accumulator = (...nums) => { return nums.reduce((acc, num) => acc * num); }; const arrayNumbers = [1, 3, 5]; console.log(accumulator(1, 2, 3)); // 6 console.log(accumulator(...arrayNumbers)); // 15 console.log(sum(1, 2, 3, 4, 5)); // 15 console.log(sum(...arrayNumbers)); // 9
3. 格式化搜索参数
获取 URL 种的搜索参数是经常要处理的功能。
// url https://www.devpoint.cn/index.shtml?name=devpoint&id=100 // 格式化 search parameters { name: "devpoint", id: "100", }
常规方式
这是大多数人使用它的方式。
const parseQuery = (search = window.location.search) => { const query = {}; search .slice(1) .split("&") .forEach((it) => { const [key, value] = it.split("="); query[key] = decodeURIComponent(value); }); return query; }; console.log(parseQuery("?name=devpoint&id=100")); // { name: 'devpoint', id: '100' }
使用 reduce
const parseQuery = (search = window.location.search) => search .replace(/(^\?)|(&$)/g, "") .split("&") .reduce((query, it) => { const [key, value] = it.split("="); query[key] = decodeURIComponent(value); return query; }, {}); console.log(parseQuery("?name=devpoint&id=100")); // { name: 'devpoint', id: '100' }
4. 反序列化搜索参数
当要跳转到某个链接并为其添加一些搜索参数时,手动拼接的方式不是很方便。如果要串联的参数很多,那将是一场灾难。
const searchObj = { name: "devpoint", id: 100, // ... }; const strLink = `https://www.devpoint.cn/index.shtml?name=${searchObj.name}&age=${searchObj.id}`; console.log(strLink); // https://www.devpoint.cn/index.shtml?name=devpoint&age=100
reduce
可以轻松解决这个问题。
const searchObj = { name: "devpoint", id: 100, // ... }; const stringifySearch = (search = {}) => Object.entries(search) .reduce( (t, v) => `${t}${v[0]}=${encodeURIComponent(v[1])}&`, Object.keys(search).length ? "?" : "" ) .replace(/&$/, ""); const strLink = `https://www.devpoint.cn/index.shtml${stringifySearch( searchObj )}`; console.log(strLink); // https://www.devpoint.cn/index.shtml?name=devpoint&age=100
5. 展平多层嵌套数组
如何展平多层嵌套数组吗?
const array = [1, [2, [3, [4, [5]]]]]; const flatArray = array.flat(Infinity); console.log(flatArray); // [ 1, 2, 3, 4, 5 ]
如果运行环境支持方法 flat ,则可以直接用,如果不支持,使用 reduce 也可以实现和flat一样的功能。
const array = [1, [2, [3, [4, [5]]]]]; const flat = (arrayNumbers) => arrayNumbers.reduce( (acc, it) => acc.concat(Array.isArray(it) ? flat(it) : it), [] ); const flatArray = flat(array); console.log(flatArray); // [ 1, 2, 3, 4, 5 ]
6. 计算数组成员的数量
如何计算数组中每个成员的个数?即计算重复元素的个数。
const count = (array) => array.reduce( (acc, it) => (acc.set(it, (acc.get(it) || 0) + 1), acc), new Map() ); const array = [1, 2, 1, 2, -1, 0, "0", 10, "10"]; console.log(count(array));
这里使用了数据类型 Map
,关于 JavaScript 的这个数据类型,有兴趣可以阅读下文:
上面代码的输出结果如下:
Map(7) { 1 => 2, 2 => 2, -1 => 1, 0 => 1, '0' => 1, 10 => 1, '10' => 1 }
7.获取一个对象的多个属性
这是一个项目开发中比较常遇见的场景。通过 API 获取后端数据,前端很多时候只需要取其中部分的数据。
// 一个有很多属性的对象 const obj = { a: 1, b: 2, c: 3, d: 4, e: 5, // ... }; // 只是想得到它上面的一些属性来创建一个新的对象 const newObj = { a: obj.a, b: obj.b, c: obj.c, d: obj.d, // ... };
这个时候可以使用
reduce
来解决。
/** * * @param {*} obj 原始对象 * @param {*} keys 需要获取的属性值列表,数组形式 * @returns */ const getObjectKeys = (obj = {}, keys = []) => Object.keys(obj).reduce( (acc, key) => (keys.includes(key) && (acc[key] = obj[key]), acc), {} ); const obj = { a: 1, b: 2, c: 3, d: 4, e: 5, // ... }; const newObj = getObjectKeys(obj, ["a", "b", "c", "d"]); console.log(newObj); // { a: 1, b: 2, c: 3, d: 4 }
8.反转字符串
反转字符串是面试中最常问到的 JavaScript 问题之一。
const reverseString = (string) => { return string.split("").reduceRight((acc, s) => acc + s); }; const string = "devpoint"; console.log(reverseString(string)); // tniopved
9.数组去重
reduce
也很容易实现数组去重。
const array = [1, 2, 1, 2, -1, 10, 11]; const uniqueArray1 = [...new Set(array)]; const uniqueArray2 = array.reduce( (acc, it) => (acc.includes(it) ? acc : [...acc, it]), [] ); console.log(uniqueArray1); // [ 1, 2, -1, 10, 11 ] console.log(uniqueArray2); // [ 1, 2, -1, 10, 11 ]
10. 模拟方法 flat
虽然现在的JavaScript有原生方法已经实现了对深度嵌套数组进行扁平化的功能,但是如何才能完整的实现扁平化的功能呢?下面就是使用 reduce
来实现其功能:
// 默认展开一层 Array.prototype.flat2 = function (n = 1) { const len = this.length; let count = 0; let current = this; if (!len || n === 0) { return current; } // 确认当前是否有数组项 const hasArray = () => current.some((it) => Array.isArray(it)); // 每次循环后展开一层 while (count++ < n && hasArray()) { current = current.reduce((result, it) => result.concat(it), []); } return current; }; const array = [1, [2, [3, [4, [5]]]]]; // 展开一层 console.log(array.flat()); // [ 1, 2, [ 3, [ 4, [ 5 ] ] ] ] console.log(array.flat2()); // [ 1, 2, [ 3, [ 4, [ 5 ] ] ] ] // 展开所有 console.log(array.flat(Infinity)); // [ 1, 2, 3, 4, 5 ] console.log(array.flat2(Infinity)); // [ 1, 2, 3, 4, 5 ]