前端小伙伴儿应该都听过reduce这个数组的方法,总结一下我在开发过程中遇到的reduce的一些好玩儿的用法
老规矩,上MDN:reduce-MDN
简单介绍一下一些重要的点
定义:reduce()
方法对数组中的每个元素执行一个由您提供的 reducer
函数(升序执行),将其结果汇总为单个返回值。
示例:
const array1 = [1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator + currentValue;
// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
// expected output: 10
// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));
// expected output: 15
reducer
函数接收4个参数:
Accumulator (acc) (累计器)
CurrentValue (cur) (当前值)
CurrentIndex (idx) (当前索引)
SourceArray (src) (源数组)
您的 reducer
函数的返回值分配给累计器,该返回值在数组的每个迭代中被记住,并最后成为最终的单个结果值
arr.reduce(callback(accumulator, currentValue, index, array), initialValue)
callback
执行数组中每个值 (如果没有提供 initialValue则第一个值除外
)的函数,包含四个参数:
`accumulator`:累计器累计回调的返回值; 它是上一次调用回调时返回的累积值,或`initialValue`。
`currentValue`:数组中正在处理的元素。
`index`(可选):数组中正在处理的当前元素的索引。 如果提供了`initialValue`,则起始索引号为0,否则从索引1起始。
`array`(可选):调用`reduce()`的数组
reduce
为数组中的每一个元素依次执行callback
函数,不包括数组中被删除或从未被赋值的元素,接受四个参数:
accumulator 累计器
currentValue 当前值
currentIndex 当前索引
array 数组
回调函数第一次执行时,accumulator
和currentValue
的取值有两种情况:
1、如果调用reduce()
时提供了initialValue
,accumulator
取值为initialValue
,currentValue
取数组中的第一个值;
2、如果没有提供 initialValue
,那么accumulator
取数组中的第一个值,currentValue
取数组中的第二个值。
注意:官方推荐在reduce使用时提供 initialValue
,为了避免错误,更多的详细理解请查阅MDN,下面就用实际的例子来玩儿一下
1、基础的累加累乘
var arr = [1, 2, 3, 4];
var sum = arr.reduce((prev, cur) => prev + cur, 0)
var mul = arr.reduce((prev, cur) => prev * cur, 1)
console.log(sum); // 10
console.log(mul); // 24
2、对象内的操作
var result = [
{
subject: 'math',
score: 10
},
{
subject: 'chinese',
score: 20
},
{
subject: 'english',
score: 30
}
];
var sum = result.reduce((prev, cur) => {
return cur.score + prev;
}, 0);
console.log(sum) //60
3、统计数组中元素出现次数
let names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
let nameNum = names.reduce((prev, cur)=>{
if(cur in prev){
prev[cur]++
}else{
prev[cur] = 1
}
return pre
}, {})
console.log(nameNum); //{Alice: 2, Bob: 1, Tiff: 1, Bruce: 1}
4、数组去重
let arr = [1,2,3,4,4,1]
let newArr = arr.reduce((prev, cur)=>{
if(!prev.includes(cur)){
return prev.concat(cur)
}else{
return prev
}
}, [])
console.log(newArr);// [1, 2, 3, 4]
// 使用特定的属性值判断去重
arrayDeduplicate<T>(arr: T[], key: string): T[] {
let result: T[] = [];
arr.reduce((prev, cur) => {
if (prev[key] !== cur[key]) {
result.push(cur);
}
return cur;
}, {});
return result;
}
5、多维数组降一维数组
let arr = [[0, 1], [2, 3], [4,[5,6,7]]]
const newArr = function(arr){
return arr.reduce((prev, cur) => prev.concat(Array.isArray(cur) ? newArr(cur) : cur), [])
}
console.log(newArr(arr)); //[0, 1, 2, 3, 4, 5, 6, 7]
6、数组和对象深克隆
const deepClone = param => {
if (typeof param !== 'object') return
if (Array.isArray(param)) {
param.reduce((prev, cur) => (cur instanceof Array ? [...prev, deepClone(cur)] : [...prev, cur]), [])
} else {
Object.entries(param).reduce(
(prev, [key, value]) => (typeof value === 'object' ? { ...prev, [key]: deepClone(value) } : { ...prev, [key]: value }),
{}
)
}
return param
}
7、封装一个同步顺序执行函数,并返回结果
let fn1 = () => {
return {
name: 'lsd',
age: 18
}
}
let fn2 = () => {
return {
name: 'lbb',
age: 19
}
}
let fn3 = () => {
return {
name: 'whh',
age: 20
}
}
let fnlist = [fn1, fn2, fn3]
let res = fnlist.reduce((prev, cur) => {
let t = cur()
if (t) {
prev.push(t)
}
return prev
}, [])
console.log(res)
8、基于7封装异步请求顺序执行,并处理请求结果
let fn1 = () => {
return new Promise((resolve, reject) => resolve(1))
}
let fn2 = () => {
return new Promise((resolve, reject) => reject())
}
let fn3 = () => {
return new Promise((resolve, reject) => resolve(2))
}
let fnlist = [fn1, fn2, fn3]
let res = fnlist.reduce((prev, cur) => {
cur().then(
data => {
if (data) {
prev.push(data)
}
},
reason => {
prev.push('失败')
}
)
return prev
}, [])
console.log(res)
// 应该在then函数中定义onResolve和onRejct函数,如果使用catch捕获错误,会进入下一次事件循环,不是同步执行;此处如果需要异步执行,请自行修改
9、模拟koa洋葱模型
// 每个中间件都能接收到core
function receiveMiddleware(middlewareList) {
//将中间件队列改造为函数层层嵌套形式
//[a,b,c,d] => a(b(c(d(core)))) By reduce
let tiggerPipe = middlewareList.reduce((a, b) => core => a(b(core)))
let tiggerPipeWitchCore = tiggerPipe(() => {
console.log('我是核心操作')
})
return tiggerPipeWitchCore
}
const VerfiyCsrfToekn = next => lastMDarg => {
console.log('验证csrf Token')
next(lastMDarg)
console.log('验证csrf Token end')
}
const VerfiyAuth = next => lastMDarg => {
console.log('验证是否登录')
next(lastMDarg)
console.log('验证是否登录 end')
}
const VerfiyRoutes = next => lastMDarg => {
console.log('验证路由匹配')
next(lastMDarg)
console.log('验证路由匹配 end')
}
let dispatch = receiveMiddleware([VerfiyCsrfToekn, VerfiyAuth, VerfiyRoutes])
dispatch()
10、带异步控制的中间件
const store = {
status: { name: '固态空气' },
getState: () => {
return this.status
},
dispatch: arg => {
console.log(`我是核心操作,参数=${arg}`)
}
}
function receiveMiddleware(middlewareList) {
//拿到中间件队列
let dispatch = store.dispatch
let middlewareAPI = {
dispatch: arg => {
dispatch(arg)
},
getState: store.getState
}
//判断中间件数量
if (middlewareList.length === 0) {
return dispatch
}
//将核心操作当作参数赋予每个中间件
middlewareList = middlewareList.map(middleware => middleware(middlewareAPI))
//将中间件队列改造为函数层层嵌套形式
//[a,b,c,d] => a(b(c(d(core)))) By reduce
let tiggerPipe = middlewareList.reduce((prev, cur) => reallyDispatch => prev(cur(reallyDispatch)))
//重写dispatch
dispatch = tiggerPipe(store.dispatch)
return dispatch
}
const VerfiyCsrfToekn = middlewareAPI => next => lastMDarg => {
console.log('验证csrf Token')
next(lastMDarg)
console.log('验证csrf Token end')
}
const VerfiyAuth = middlewareAPI => next => lastMDarg => {
console.log('验证是否登录')
next(lastMDarg)
console.log('验证是否登录 end')
}
const VerfiyRoutes = middlewareAPI => next => lastMDarg => {
console.log('验证路由匹配')
next(lastMDarg)
console.log('验证路由匹配 end')
}
const asyncMiddleware = middlewareAPI => next => lastMDarg => {
console.log('异步中间件-start')
if (typeof lastMDarg === 'function') {
lastMDarg(middlewareAPI)
} else {
next(lastMDarg)
console.log('异步中间件-end')
}
}
let dispatch = receiveMiddleware([VerfiyCsrfToekn, VerfiyAuth, VerfiyRoutes, asyncMiddleware])
let asyncFun = middlewareAPI => {
setTimeout(() => {
let test = '我是固态空气'
middlewareAPI.dispatch(test)
console.log(middlewareAPI.getState())
}, 3000)
}
dispatch(asyncFun)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。