前言最近面试的时候有同学被问到这个问题,回答的不是很好,所以决定临时写一篇关于JavaScript中数组方法的issue(没想到呢,没想到,明明快过年了,需求量还是很大的,就不说了,因为工作太忙,写代码比写文字容易)。1.数据结构相关的方法有基本数据结构的同学应该知道栈和队列这两种数据结构(不知道的同学可以自行查看,没有文章推荐~)。在JavaScript的数组方法中,有一些方法适用于数组实现的栈和队列这两种数据结构。1.1Array.prototype.pushpush()方法向数组末尾添加一个或多个元素,并返回数组的新长度。是否改变原数组:是参数:elementN:添加到数组末尾的元素返回值:数组添加元素后length属性的值该方法类似于栈中的push方法,将其压入一个元素入栈:conststack=[];stack.push(1);//1console.log(stack);//[1]conststack1=[];stack1.push(1,2);//2console.log(stack1);//[1,2]1.2Array.prototype.poppop()方法从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度。是否改变原数组:是参数:无返回值:从数组中删除的元素(数组为空时返回undefined)该方法类似于栈中的pop方法,弹出栈顶元素:conststack=[1,2,3,4,5];stack.pop();//5console.log(stack);//[1,2,3,4]conststack1=[];stack1.pop();//未定义控制台。log(stack1);//[]1.3Array.prototype.unshiftunshift()方法向数组的开头添加一个或多个元素,并返回数组的新长度(该方法修改了原数组)。是否改变原数组:是参数:elementN:要添加到数组开头的一个或多个元素返回值:添加元素到数组元素入队列后的length属性的值:constqueue=[3,4,5];queue.unshift(2);//4console.log(队列);//[2,3,4,5]constqueue1=[3,4,5]];queue1.unshift(1,2);//5console.log(queue1);//[1,2,3,4,5]1.4Array.prototype.shiftshift()方法从数组中移除第一个元素,并返回该元素的值。此方法更改数组的长度。是否改变原数组:是参数:无返回值:从数组中删除的元素(数组为空时返回undefined)这个方法有点类似于队列的dequeue操作,也有点类似于的pop操作堆栈:constqueue=[3,4,5];queue.shift();//3console.log(queue);//[4,5]constqueue1=[];queue1.shift();//undefinedconsole。log(queue1);//[]1.5总结其实之所以说相似或者说有些相似,是因为JavaScript本身并没有提供严格的方法来实现队列或者栈等数据结构,而是通过灵活的使用这4个API,其实可以更灵活的实现类似队列或者栈的功能。但是如果你真的想在生产环境中使用这4个API来模拟栈或者队列,不妨稍微封装一下。有兴趣的同学不妨在学习了栈和队列的知识后尝试一下。1.6尝试自己实现?让我们尝试实现上面的API。1.6.1仿push先来个push看看:Array.prototype.myPush=function(...args){lettop=this.length;for(leti=0;i0&&this.length--;returntopEle;};conststack=[1,2,3,4,5];stack.myPop();//5console.log(stack);//[1,2,3,4]conststack1=[];stack1.myPop();//undefinedconsole.log(stack1);//[]1.7总结题外话,写个方法,不管是模仿还是自研。首先要确定的是函数的入参和返回值,后面不再赘述。同时,我个人倾向于编写纯函数。至于仿unshift和shift,这里就不写了,会有点麻烦,不过也不算太复杂。2.order相关的方法2.1Array.prototype.sortsort()方法使用in-place算法对数组的元素进行排序并返回数组。默认排序顺序是在将元素转换为字符串然后比较它们的UTF-16代码单元值序列时构造的。是否改变原数组:是参数:compareFunction:用于指定一个函数按一定顺序排列。如果省略,则元素按转换后字符串的各个字符的Unicode位点排序。返回值:排序后的数组(原数组)compareFunction参数:a:用于比较的第一个元素b:用于比较的第二个元素返回值:number说明:如果compareFunction(a,b)小于0,则a为排在b之前如果compareFunction(a,b)等于0,a和b的相对位置不会改变如果compareFunction(a,b)大于0,b会排在a之前总之,sort方法Accepts一个回调函数。这个回调函数的入参是数组中要比较的两个元素a和b,返回值是一个数字。sort根据number的值对数组执行上述操作。看一看:栗子::constnumbers=[4,2,5,1,3];numbers.sort(function(a,b){returna-b;});//[1,2,3,4,5]让我们解决这个问题。如果要展开的内容太多,这里就不展开了。2.2Array.prototype.reversereverse()方法将数组中元素的位置反转,返回数组。数组的第一个元素成为最后一个元素,数组的最后一个元素成为第一个元素。此方法将更改原始数组。是否改变原数组:是参数:无返回值:倒序数组(原数组)没什么好说的,就是倒序数组,下面看看:chestnut::constarr=[1,2,3,4,5];arr.reverse();//[5,4,3,2,1]3.遍历相关方法3.1Array.prototype.everyevery()方法测试数组中的所有元素是否都可以通过某个测试指定的功能。它返回一个布尔值。是否改变原数组:无参数:callback:回调函数thisArg:执行回调时使用的this值返回值:boolean(是否所有元素都通过回调测试)回调参数:element:执行回调时数组的当前元素callback是否执行index:current元素索引array:调用方法的当前数组返回值:boolean说明:判断当前元素是否满足回调的判断条件例如:chestnut::consttest=[1,2,3,4,5];constisMoreThanThree=(num)=>{returnnum>3;};test.every(isMoreThanThree);//false3.2Array.prototype.somesome()方法测试数组中是否至少有一个元素通过提供功能测试。它返回一个布尔类型的值。是否改变原数组:无参数:callback:回调函数thisArg:执行回调时使用的this值返回值:boolean(是否至少有一个元素通过回调测试)回调参数:element:当前元素执行回调时的arrayindex:当前元素的索引array:调用方法的当前数组返回值:boolean描述:判断当前元素是否满足回调的判断条件例如:栗子::consttest=[1,2,3,4,5];constisMoreThanThree=(num)=>{returnnum>3;};test.some(isMoreThanThree);//true3.3Array.prototype.findfind()方法返回值满足提供的测试函数的数组中的第一个元素。否则返回未定义。是否改变原数组:无参数:callback:回调函数thisArg:执行回调时使用的this值返回值:数组中第一个满足提供的测试函数的元素的值,否则返回undefined。回调参数:element:执行回调时数组的当前元素index:当前元素的索引array:调用方法的当前数组返回值:boolean描述:判断当前元素是否满足判断条件callback例如:chestnut::consttest=[1,2,3,4,5];constisThree=(num)=>{returnnum===3;};constisSeven=(num)=>{returnnum===7;};test.find(isThree);//3test.find(isSeven);//undefined3.4Array.prototype.findIndexfindIndex()方法返回数组中第一个满足提供的测试函数的元素的索引.如果没有找到相应的元素,则返回-1。和上面的很像,略。3.5Array.prototype.forEachforEach()方法为数组的每个元素执行一次给定函数。是否改变原数组:否参数:callback:回调函数thisArg:执行回调时使用的this值返回值:undefinedCallback参数:element:执行回调时数组的当前元素index:当前的索引element数组:调用方法的当前数组,返回值:不用想你刚接触前端时怎么总是用错方法。无非是使用命令式for循环的习惯,编码习惯不好。其实新生可以记住:两个no——不改变原数组,没有返回值(返回值是undefined)。嗯,比如:栗子::consttest=[1,2,3,4,5];constlog=(val)=>{console.log(val);};test.forEach(log);//顺序打印123453.6Array.prototype.mapmap()方法创建一个新数组,其结果是数组中的每个元素都是调用所提供函数的返回值。是否改变原数组:无参数:callback:回调函数thisArg:执行回调时使用的this值返回值:数组(对原数组的每个元素执行回调函数的结果组成的新数组。)callback参数:element:数组执行回调时的当前元素index:当前元素的索引array:调用方法的当前数组返回值:组成新数组的元素。我以前总是把这个方法和forEach混淆。其实现在看来,我英语还是没学好。.map有映射的意思,简单来说就是表达。所以,map会生成一个与原数组有映射关系的数组。然后举个例子:栗子::consttest=[1,2,3,4,5];constdouble=(num)=>{returnnum*2;};constdoubleList=test.map(double);console.log(doubleList);//[2,4,6,8,10]3.7Array.prototype.filterfilter()方法创建一个新数组,其中包含由提供的函数实现的测试的所有元素。是否改变原数组:否参数:callback:回调函数thisArg:执行回调时使用的this值返回值:Array(由测试通过的元素组成的新数组,如果没有数组元素通过测试,则返回空数组.)回调参数:element:执行回调时数组的当前元素index:当前元素的索引array:调用方法的当前数组返回值:boolean描述:判断元素是否通过测试的命名这个方法也很容易理解,例如:chestnut::consttest=[1,2,3,4,5];constisMoreThanThree=(num)=>{returnnum>3;};constmoreThanThreeList=test.filter(isMoreThanThree);console.log(moreThanThreeList);//[4,5]3.8Array.prototype.reducedreduce()方法对数组中的每个元素执行你提供的reducer函数(按升序排列),并将其结果聚合到一个单一的返回值。是否改变原数组:无参数:callback:回调函数initialValue:第一次调用回调函数时第一个参数的值。如果未提供初始值,将使用数组中的第一个元素。在没有初始值的空数组上调用reduce会抛出错误。返回值:函数回调参数累加处理的结果:accumulator:累加器累加回调的返回值;是上次调用回调时返回的累加值,或者initialValueelement:执行回调时数组的当前元素index:当前元素的索引array:调用方法的当前数组返回值:boolean说明:这种判断元素是否通过测试的方法非常非常强大。可以说它几乎可以适用于任何迭代数组并获取返回值的场景。例如:数学计算:累加、阶乘、求最大值和最小值……数组操作:去重、展平……函数式:管道(从左到右执行函数组合)等。这里我们给出一个实现管道:板栗::constpipe=(...fns)=>{return(arg)=>{returnfns.reduce((res,fn)=>{returnfn(res);},arg);};};3.9小结不难发现API与数组迭代等方法过于相似,但实际上这些API是非常语义化的,用多了就会熟悉。接下来,您不妨自己实现一个?3.10仿reduceArray.prototype.myReduce=function(fn,initialValue){letret=initialValue||this[0];letidx=initialValue?0:1;while(idx{returnnum1+num2;};test.myReduce(添加);//15test.myReduce(add,10);//25比较粗,其他方法类似,有兴趣的同学可以自己写,写了多少也能加深理解。4其他方法4.1Array.prototype.flatflat()该方法会按照指定的深度递归遍历数组,将所有元素和遍历的子数组中的元素组合成一个新的数组并返回。是否改变原数组:否参数:depth(指定提取嵌套数组的结构深度,默认值为1。)返回值:Flattenedarray(包含数组和子数组中所有元素的新数组.)曾经一直觉得压扁的方法在实际生产中用处不大。直到后来,当我维护的业务变得复杂,需要用地图对数据进行分类管理,最后取出来合并时,才发现这个方法还蛮香的。例如:栗子::consttest=[1,[2,3],[4,[5,6]]];test.flat(1);//[1,2,3,4,[5,6]]]test.flat(2);//[1,2,3,4,5,6]自己实现?事实上,我非常喜欢使用堆栈来实现扁平化的想法。MDN上有这个版本。但是我看到很少有相关的文章提到这个方法。consttest=[1,[2,3],[4,[5,6]]];constflatten=(list)=>{conststack=[...list];constret=[];while(stack.length){consttopElem=stack.pop();if(Array.isArray(topElem)){stack.push(...topElem);}else{ret.push(topElem);}}returnret.reverse();};展平(test);//[1,2,3,4,5,6]其实思路比较容易想到:将数组展平显然是一个需要深度搜索的过程,而深度搜索需要用到stack,thenafter堆栈模板就可以了。可能需要稍微转一下的就是数组最后的反转。当然,如果按照常理来写,那么每一步入栈都是一个逆元素。5.结语其实还是有很多方法是比较常用的,但是限于篇幅,这里暂时不展开,例如:Array.prototype.spliceArray.prototype.joinArray.prototype.concat数组太常见的数据结构,数组方法一定要背。其实我个人是不喜欢写那种文章的,但是写下来之后,又不可避免的写那种文章。本来这篇文章本来应该在两周前的周末发出的,但是因为最近工作实在太忙,所以延迟了。坚持真的不是一件容易的事。写文章和学习也是一样。半壁老师这两天发了一首新歌,来一首半壁老师的歌《光明》。有些东西只有坚持才能看出来,但是在我看来,前端学习真的没有那么难,可能只是方法不对而已。希望每个人都能坚持下去,在远方找到属于自己的光芒。