背景自上一篇技术文章《1.5万字概括ES6全部特性》发表以来,已经4个月没有技术文章输出了。哈哈,不是不想写,是太忙了。这段时间每天不是上班就是加班,没有属于自己的时间。这篇文章也是业余时间完成的,希望大家喜欢,感谢大家一直以来对我的支持。本文首发于《搜狐科技产品》公众号。第一个内容与博客内容略有不同。今天早上发布博客内容时,reduce被添加为ES5中新的常规数组方法之一。与forEach、filter、map相比,在实际使用中似乎被忽略了,发现身边的人很少用到,导致这么强大的方法逐渐被埋没。如果经常使用reduce,哪有这么好用的!还是要把他从尘土里拿出来擦干净,把它的高级用法呈现给大家。一个如此有效的方法不应该被群众埋没。下面简单介绍一下reduce的语法。具体可以参考MDN上关于reduce()的说明。定义:对数组中的每个元素执行一个自定义的累加器,并将结果汇??总成一个单一的返回值形式:array.reduce((t,v,i,a)=>{},initValue)参数回调:回调函数(required)initValue:初始值(可选)回调函数参数total(t):累加器计算后的返回值(必填)value(v):当前元素(必填)index(i):当前元素的索引element(optional)array(a):当前元素所属的数组对象(optional)过程以t作为累加结果的初始值,如果t不设置,则从数组的第一个元素开始遍历作为初始值,使用累加器处理v,累加v到t的映射结果,结束本次循环,返回t进入下一次循环,重复上述操作,直到数组最后一个元素遍历完成,返回最终treduce的本质是累加filter作用于array成员一条一条,上次输出的值作为下一次输入的值。下面举个简单的栗子,看看reduce的计算结果。constarr=[3,5,1,4,2];consta=arr.reduce((t,v)=>t+v);//相当于constb=arr.reduce((t,v)=>t+v,0);reduce本质上是一个累加器函数,它使用用户自定义的累加器对数组成员进行自定义累加,得到累加器生成的一个值。另外,reduce还有一个弟弟,reduceRight。这两个方法的作用其实是一样的,只是reduce是升序执行,reduceRight是降序执行。在空数组上调用reduce()和reduceRight()将不会执行其回调函数。可以认为reduce()对于空数组是无效的。高级用法光是上面简单的栗子还不足以说明什么是reduce。为了展示reduce的魅力,我为大家提供了25个场景来应用reduce的高??级用法。一些高级用法可能需要结合其他方法来实现,这为reduce的多样化提供了更多的可能性。一些示例代码的编写可能有点棘手。如果你不习惯,你可以把它整理成你自己习惯的写作。累加乘法函数Accumulation(...vals){returnvals.reduce((t,v)=>t+v,0);}functionMultiplication(...vals){returnvals.reduce((t,v)=>t*v,1);}Accumulation(1,2,3,4,5);//15Multiplication(1,2,3,4,5);//120权重和constscores=[{score:90,subject:"chinese",weight:0.5},{score:95,subject:"math",weight:0.3},{score:85,subject:"english",weight:0.2}];constresult=scores.reduce((t,v)=>t+v.score*v.weight,0);//90.5代替reversefunctionReverse(arr=[]){returnarr.reduceRight((t,v)=>(t.push(v),t),[]);}Reverse([1,2,3,4,5]);//[5,4,3,2,1]替换map和filterconstarr=[0,1,2,3];//替换地图:[0,2,4,6]consta=arr.map(v=>v*2);constb=arr.reduce((t,v)=>[...t,v*2],[]);//替换过滤器:[2,3]constc=arr.filter(v=>v>1);constd=arr.reduce((t,v)=>v>1?[...t,v]:t,[]);//替换map和filter:[4,6]conste=arr.映射(v=>v*2).filter(v=>v>2);constf=arr.reduce((t,v)=>v*2>2?[...t,v*2]:t,[]);而不是some和everyconstscores=[{score:45,subject:"chinese"},{score:90,subject:"math"},{score:60,subject:"english"}];//代some:至少一级constisAtLeastOneQualified=scores.reduce((t,v)=>v.score>=60,false);//true//代表每一个:全部合成constisAllQualified=scores.reduce((t,v)=>t&&v.score>=60,true);//false分组分割functionChunk(arr=[],size=1){returnarr.length?arr.reduce((t,v)=>(t[t.length-1].length===size?t.push([v]):t[t.length-1].push(v),t),[[]]):[];}constarr=[1,2,3,4,5];块(arr,2);//[[1,2],[3,4],[5]]数过滤函数Difference(arr=[],oarr=[]){returnarr.reduce((t,v)=>(!oarr.includes(v)&&t.push(v),t),[]);}constarr1=[1,2,3,4,5];constarr2=[2,3,6]差异(arr1,arr2);//[1,4,5]数组填充functionFill(arr=[],val="",start=0,end=arr.length){if(start<0||start>=end||end>arr.length)returnarr;re??turn[...arr.slice(0,start),...arr.slice(start,end).reduce((t,v)=>(t.push(val||v),t),[]),...arr.slice(end,arr.length)];}constarr=[0,1,2,3,4,5,6];Fill(arr,"aaa",2,5);//[0,1,"aaa","aaa","aaa",5,6]ArrayflatfunctionFlat(arr=[]){returnarr.reduce((t,v)=>t.concat(Array.isArray(v)?Flat(v):v),[])}constarr=[0,1,[2,3],[4,5,[6,7]],[8,[9,10,[11,12]]]];Flat(arr);//[0,1,2,3,4,5,6,7,8,9,10,11,12]数组去重函数Uniq(arr=[]){returnarr.reduce((t,v)=>t.includes(v)?t:[...t,v],[]);}constarr=[2,1,0,3,2,1,2];Uniq(arr);//[2,1,0,3]数组最大最小值functionMax(arr=[]){returnarr.reduce((t,v)=>t>v?t:v);}functionMin(arr=[]){returnarr.reduce((t,v)=>t(v.forEach((w,i)=>t[i].push(w)),t),Array.from({length:Math.max(...arr.map(v=>v.length))}).map(v=>[]));}constarr=[["a",1,true],["b",2,false]];解压(arr);//[["a","b"],[1,2],[true,false]]统计数组成员个数functionCount(arr=[]){returnarr.reduce((t,v)=>(t[v]=(t[v]||0)+1,t),{});}constarr=[0,1,1,2,2,2];Count(arr);//{0:1,1:2,2:3}这个方法就是字符计数和字数计数的原理。输入参数时,将字符串处理成数组,记录数组成员functionPosition(arr=[],val){returnarr.reduce((t,v,i)=>(v===val&&t.push(i),t),[]);}constarr=[2,1,5,4,2,1,6,6,7];Position(arr,2);//[0,4]数组成员特征分组函数组(arr=[],key){returnkey?arr.reduce((t,v)=>(!t[v[key]]&&(t[v[key]]=[]),t[v[键]].push(v),t),{}):{};}constarr=[{area:"GZ",name:"YZW",age:27},{area:"GZ",name:"TYJ",age:25},{area:"SZ",name:"AAA",age:23},{area:"FS",name:"BBB",age:21},{area:"SZ",name:"CCC",age:19}];//根据Group(arr,"area");//{GZ:Array(2),SZ:Array(2),FS:Array(1)}统计数组成员包含的关键字functionKeyword(arr=[],keys=[]){returnkeys.reduce((t,v)=>(arr.some(w=>w.includes(v))&&t.push(v),t),[]);}consttext=["今天天气真好,我想去钓鱼","边看电视边做作业",“小明喜欢同桌小红,我也喜欢后桌的小君,我好浮躁”,“最近上班喜欢钓鱼的人太多了,代码写得不好,我在想事情”];constkeyword=["懒惰","喜欢","睡觉","钓鱼","好","边","明天"];Keyword(text,keyword);//["喜欢","钓鱼","好","side"]字符串反转函数ReverseStr(str=""){returnstr.split("").reduceRight((t,v)=>t+v);}conststr="reduceisthebest";ReverseStr(str);//"力最ecuder"数字千差分函数ThousandNum(num=0){conststr=(+num).toString().split(".");constint=nums=>nums.split("").reverse().reduceRight((t,v,i)=>t+(i%3?v:`${v},`),"").replace(/^,|,$/g,"");constdec=nums=>nums.split("").reduce((t,v,i)=>t+((i+1)%3?v:`${v},`),"").replace(/^,|,$/g,"");返回str.length>1?`${int(str[0])}.${dec(str[1])}`:int(str[0]);}ThousandNum(1234);//"1,234"ThousandNum(1234.00);//"1,234"ThousandNum(0.1234);//"0.123,4"ThousandNum(1234.5678);//"1,234.567,8"异步累加asyncfunctionAsyncTotal(arr=[]){returnarr.reduce(async(t,v)=>{constat=awaitt;consttodo=awaitTodo(v);at[v]=todo;returnat;},Promise.resolve({}));}constresult=awaitAsyncTotal();//需要用到斐波那契数列函数Fibonacci(len=2){constarr=[...newArray(len).keys()];returnarr.reduce((t,v,i)=>(i>1&&t.push(t[i-1]+t[i-2]),t),[0,1]);}Fibonacci(10);//[0,1,1,2,3,5,8,13,21,34]URL参数反序列化函数ParseUrlSearch(){returnlocation.search.replace(/(^\?)|(&$)/g,"").split("&").reduce((t,v)=>{const[key,val]=v.split("=");t[key]=decodeURIComponent(val);return;},{});}//假设URL为:https://www.baidu.com?age=25&name=TYJParseUrlSearch();//{age:"25",name:"TYJ"}URL参数序列化函数StringifyUrlSearch(search={}){returnObject.entries(search).reduce((t,v)=>`${t}${v[0]}=${encodeURIComponent(v[1])}&`,Object.keys(search).length?":"").replace(/&$/,"");}StringifyUrlSearch({age:27,name:"YZW"});//"?age=27&name=YZW"返回指定的对象键函数GetKeys(obj={},keys=[]){returnObject.keys(obj).reduce((t,v)=>(keys.includes(v)&&(t[v]=obj[v]),t),{});}consttarget={a:1,b:2,c:3,d:4};constkeyword=["a","d"];GetKeys(目标,关键字);//{a:1,d:4}对象数组constpeople=[{area:"GZ",name:"YZW",age:27},{area:"SZ",name:"TYJ",age:25}];constmap=people.reduce((t,v)=>{const{name,...rest}=v;t[name]=rest;returnt;},{});//{YZW:{...},TYJ:{...}}ReduxCompose函数原理functionCompose(...funs){if(funs.length===0){returnarg=>arg;}if(funs.length===1){returnfuns[0];}returnfuns.reduce((t,v)=>(...arg)=>t(v(...arg)));}兼容性和性能都很好用,但是兼容性如何呢?在Caniuse上搜索一下,兼容性绝对好,你可以在任何项目上大胆使用,不要吝啬你的想象力,充分发挥reduce的compose技能。对于时不时做一些累加的函数,reduce绝对是首选方法。另外,可能有同学会问,reduce的性能如何?接下来我们对for-in、forEach、map、reduce这四个方法同时进行1~100000的累加运算,看看这四个方法各自的执行时间。//创建数组constlist=[...newArray(100000).keys()];//for-inconsole.time("for-in");letresult1=0;for(leti=0;i(result2+=v+1));console.log(result2);console.timeEnd("forEach");//mapconsole.time("map");letresult3=0;list.map(v=>(result3+=v+1,v));console.log(result3);console.timeEnd("map");//reduceconsole.time("reduce");constresult4=list.reduce((t,v)=>t+v+1,0);console.log(result4);console.timeEnd("reduce");累计运行执行时间for-in6.719970703125msforEach3.696044921875msmap3.554931640625msreduce2.806884765625ms以上代码在MacBookPro201915寸,16G内存,512G闪存,Chrome79。以上代码在不同机器、不同环境下执行可能存在差异。我同时测试了多台机器和多个浏览器,连续做了10多次操作,发现reduce整体平均执行时间还是略快于其他三种方式,所以可以用有信心的!本文更多的是关于reduce的使用技巧。如果对reduce的兼容性和性能有疑问,可以参考相关资料进行验证。结论和最后的结论几乎一样。如果我想到任何遗漏的reduce高级用法,我会继续完成这篇文章。同时也希望小伙伴们能够对文章的要点进行补充或者提出自己的看法。欢迎在下方评论或补充,喜欢的可以点赞或收藏,保证开发中能用得上。