最近学习react 高阶组件整得我有点难受,一度怀疑自己的智商,虽然知道高阶组件和高阶函数其实是一类东西,但是就是带入不了,所以还是静下心来,好好撸一下高阶函数,凡事还是不能操之过急,太着急的结果往往不如人意。
开始reduce之前,附上今天看到的一段代码
1 2 3 4 5 6 7 8 9 10 11
| let arr = [1,2,3]; arr.reduce((pre,cur)=>{ pre.then(()=>{ return new Promise(resolve=>{ setTimeout(() => { resolve(console.log(cur)) }, 1000); }) }) return pre; },Promise.resolve())
|
猜猜这段代码实现了啥,刚看到的时候给我给整蒙了,不好意思,奈何道行太浅,我看不懂~~~~
所以决定突破高阶组件就从这个reduce函数开始吧,以前真不怎么去用reduce,一直理解成累加器,今天才知道大错特错,准确点来说,应该是一个累计器,自我感觉这词也不是很准确。
reduce介绍
首先reduce是一个高阶函数,它的参数可以是两个参数,第一个参数是函数,第二个参数是初始值,这个初始值很重要,待会再说。先说下第一个参数函数,这个参数函数呢它自己也带有四个参数(pre,value,index, arr),reduce会对arr中的每一个值调用这个参数函数。现在说下reduce的第二个参数,它是函数参数pre的初始值,如果参数函数有返回值,就可以利用reduce的第二个参数去处理这个返回值,具体怎么处理后面细细道来。
现在先来看一段代码
1 2 3 4 5
| let arr = [1,2,3]; arr.reduce((pre,current)=>{ console.log(cur); },9) //输出:1,2,3
|
可以看到这段代码reduce的参数函数里面并没有返回值,所以不管reduce的第二个参数是什么,它只是对cur值每个current值进行了console.log处理。
这个效果是不是和forEach的功能一摸一样,我们现在用它来实现forEach功能
实现forEach
1 2 3 4 5 6 7 8 9 10 11 12
| const arr = [1, 2, 3]; function forEach(arr,callback){ arr.reduce((pre, current, index,array)=>{ //直接执行callback,这个回调函数在forEach执行的时候直接作为参数写进去 //这也能解释为什么forEach没有返回值 callback(current, index,array) },0) }
forEach(arr, (value, index, array)=>{ console.log(value); })
|
实现map
1 2 3 4 5 6 7 8 9 10 11 12 13
| const arr = [1, 2, 3];
function map(arr,callback){ return arr.reduce((pre, current, index,array)=>{ let result= callback(current, index,array) pre.push(result) return pre; },[]) } let newArr =map(arr, (value, index, array)=>{ return value*10; }) console.log(newArr);//[10,20,30]
|
这里可以看出map和forEach的区别,map具有返回值,而foreEach没有返回值,所以forEach更适合对数据的一次性处理,而map则适合于需要对返回值做二次处理的场景。(其实我觉得如果抛开返回值只是对数组数据处理一下,这两个没啥区别,直接用,纠结个啥)
实现filter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const arr = [1, 2, 3];
function filter(arr,callback){ //这里不要忘记下面要将整个结果返回回去,因为filter是一个函数,要有返回值, //下面返回的pre是reduce函数处理的结果 return arr.reduce((pre, current, index,array)=>{ //这里result就是每次callback执行后的返回值,为true或false let result= callback(current, index,array) if(result){ pre.push(current) } return pre; },[])
} let newArr =filter(arr, (value, index, array)=>{ return value>1; }) console.log(newArr);
|
上面的代码中我们给pre的初始值是一个空数组,经过判断回调函数的返回值,将arr中大于1的值放到pre中。
实现some
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const arr = [1, 2, 3]; function some(arr,callback){ return arr.reduce((pre, current, index,array)=>{ let result= callback(current, index,array) if(result){ pre=result; } return pre; },false)
} let newArr =some(arr, (value, index, array)=>{ return value>2; }) console.log(newArr);
|
some函数是数组里面有一个值符合回调函数的条件就返回true,将pre的初始值设置为false可以保证当数组里面所有的值都不符合条件的时候返回false.
实现every
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const arr = [1, 2, 3]; function every(arr,callback){ let flag=true; return arr.reduce((pre, current, index,array)=>{ let result= callback(current, index,array) if(!result){ flag=false; } return flag; },)
} let newArr =every(arr, (value, index, array)=>{ return value>0; }) console.log(newArr);
|
every的实现其实不用pre的值,但需要一个flag来标记。遍历数组里面的值,如果有一个不符合直接返回false。
reduce常见的应用场景
求和
1 2 3 4 5 6 7 8
| const subjectSummary = [ {id: 1, subject: '语文', score: 86}, {id: 2, subject: '数学', score: 93}, {id: 2, subject: '英语', score: 95}, ]; const scoreSum = subjectSummary.reduce((pre, current) => { return pre + current.score; }, 0);
|
数组去重
1 2 3 4 5 6 7
| const arr = [1, 2, 3, 2, 3, 1, 8, 5]; const newArr = arr.reduce((pre, current) => { if (!pre.includes(current)) { pre.push(current); } return pre; }, []);
|
统计字符串出现次数
1 2 3 4 5 6 7 8 9
| const str = 'aabdddeffghhhhyyy'; const countMapper = str1.split('').reduce((pre, current) => { if (pre.hasOwnProperty(current)) { pre[current] += 1; } else { pre[current] = 1; } return pre; }, {});
|
分组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const menuGroup = [ {id: 1, name: '用户中心',type: 1}, {id: 2, name: '设置', type: 2}, {id: 3, name: '消息中心', type: 2}, {id: 4, name: '我的', type: 1}, ];
const menuMap = menuGroup.reduce((pre, current) => { if (current.type === 1) { pre.menuList.push(current); } if (current.type === 2) { pre.subMenuList.push(current); } return pre; }, {menuList: [], subMenuList: []});
|
综合这几个例子看下来,我感觉pre更像是一个处理器容器。pre的初始值和pre调用的函数决定了pre将如何处理参数函数中的值。
最后,我们来分析一下刚开始那段让人头疼代码
1 2 3 4 5 6 7 8 9 10 11
| let arr = [1,2,3]; arr.reduce((pre,cur)=>{ pre.then(()=>{ return new Promise(resolve=>{ setTimeout(() => { resolve(console.log(cur)) }, 1000); }) }) return pre; },Promise.resolve())
|
这段代码实现的是将1、2、3每隔一秒输出。
pre的初始值是一个Promise.resolve(),同过调用then来处理pre的resolve状态,而这个then(参数也是一个函数)它将cur的值打印出来并且返回一个promise,且状态为resolve(),那么在下一次执行参数函数的时候,这个新返回的promise又会再一次调用then………. ,有点一直调用then的意味,then就是对返回的新Promise做处理。