用好 JS 原生 API 系列之数组
最近工作做数据交互展示,常和数据打交道,而随之而来的就是遇见后端传来的各种各样的数组,我需要用各式各样的方法来变换这些数据,来最好的展示这些数据;很多东西久了没用就容易忘,自己也是边查边用,这篇文章算是自己这一周学习的知识的总结。当然你也可以打看 MSDN 查看更标准的叙述
数组原生的 API
那些需要知道的特性:
- 数组在 JS 中是对象的一种,所以数组也是引用类型,所以操作时要小心,时刻记住你操作的不是一个普通类型;
- 每个数组都自带一个 length 属性,这个属性很特别,可读也可写。JS 函数的 arguments 因为也拥有 length 属性,所以其被称为类数组对象。
这里不会提到所有的 API,因为真的太多了。我只提我最近常用的,还有被人们常说的增删查改。
API 之增删查改
我以前听说增删查改(CRUD),是 sql 语言。但有一次参加面试,面试官问:说说你知道的 JS 数组增删查改,那数组的增删查改是那些呢?说真的,当时我一脸懵逼,我还以为数组还能用 sql 语句操作
增加:push,unshift,还有通常我们常用的 arr[arr.length] = newChild;
首先我们有一个数组 arr= [1,2,3,4,5,6]
push 被称为栈操作,即栈顶入,栈顶出,所以当我们采用 arr.push(7),得到的结果是 arr= [1,2,3,4,5,6,7],其相对于 arr[arr.length] = 7;
unshift 和 push 相反,即栈底入,所以当我们做和上面相似的操作,即 arr.push(7),得到的结果是 arr= [7,1,2,3,4,5,6]
至于 arr.push(3.5)和 arr.unshift(3,5)答案是多少,你可以自己亲自尝试一下;
删除:pop(对应 push),arr.pop()操作后,arr 是 arr= [1,2,3,4,5];shift(对应 unshift),arr.shift()操作后,arr= [2,3,4,5,6];操作 length,当我们 arr.length = 4 操作后,得到 arr =[1,2,3,4];
这里提一句,使用 delete arr[arr.length-1],并不能删除最后一个元素,而只是将最后一个元素的赋值去除,值为 undefined,且其 length 未变.
查:很多时候我们都采用 for 循环遍历加比较,来查找某个元素在数组中的索引,但其实 js 是支持 indexOf 方法的,当我们进行 arr.indexOf(3),其会返回结果 2,进行 arr.indexOf(9),其返回的结果就是-1,和字符串的 indexOf 方法何其相似,所以也有相对应的 lastIndexOf()方法;
改:改在数组操作中很常用,也很直接,这个就不长述了。
这里说三个重要的方法 concat,slice 和 splice,concat 主要做数组拼接(我经常用它做数组的深拷贝);slice 主要做数组截取,而 splice 几乎能完成上述所有的 CUD 操作,之所以要把他们分开提,是因为这两个方法操作较复杂,其 cancat 与 slice 并不是对数组本身的操作,而是会产生一个新的 Array 数组,被操作的数组并没有改变;而 splice 方法,是直接对数组进行操作,仅当参数删除或替换元素操作时,会返回一个新的数组,其包含的元素就是返回的元素。
concat 方法:数组的拼接,array.concat([item1[, item2[, . . . [, itemN]]]]) ,array 指被拼接的对象数组,item 为数组。但我用的最多的就是数组的深拷贝,看实例:
var hege = ["Cecilie", "Lone"]; var stale = ["Emil", "Tobias", "Linus"]; var kai = ["Robin"]; var children = hege.concat(stale,kai); console.log(children) //打印["Cecilie", "Lone","Emil", "Tobias", "Linus","Robin"] var deepArr = stale.concat([]);//数组深拷贝; deepArr.push('Denzel'); console.log(deepArr); //打印["Emil","Tobias", "Linus","Denzel"] console.log(stale); //打印["Emil", "Tobias", "Linus"];
slice 方法:arrayObj.slice(start, [end]),从方法的描述可知,其可接受两个参数,start 即截取开始的位置,end 截取结束的位置,参数可选,如果没有,截取的位置是直到数组末尾,但需要注意的是,start 位置的元素是被截取的,而 end 位置的元素是不包含的,只截取该元素前一个元素。
var arr= [1,2,3,4,5,6] var newArr = arr.slice(0,arr.length); console.log(newArr) //打印 [1,2,3,4,5,6] newArr = arr.slice(3,5); console.log(newArr) //打印 [4,5]
其实前面都不复杂,复杂的是参数为负的时候,规则是这样的(来源于 MSDN):如果 start 为负,则将其视为 length + start,其中 length 为数组的长度。如果 end 为负,则将其视为 length + end,其中 length 为数组的长度。如果省略 end,则将一直提取到 arrayObj 的结尾。如果 end 出现在 start 之前,则不会将任何元素复制到新数组中。
var arr= [1,2,3,4,5,6] var newArr = arr.slice(0,-1); console.log(newArr) //打印 [1,2,3,4,5] newArr = arr.slice(-3,-1); console.log(newArr) //打印 [4,5]
splice 方法:arrayObj.splice(start, deleteCount, [item1[, item2[, . . . [,itemN]]]]),相比 slice,其复杂太多,所以几乎能完成所有的 cud 操作,但与其不同的是,上面的方法都只能在数组的头和尾上进行操作,splice 能完成任意位置的 cud 操作;其 arrayObj 参数为必需,且必须是一个 Array 对象;start 参数必需,指数组中移除元素操作的起点,从 0 开始,deleteCount 参数必需,指要移除的元素的个数,上面提到的返回数组,其数组元素的个数与 deleteCount 相等,当 deleteCount=0 时,其返回一个空数组;item1, item2,. . ., itemN 可选,指插入数组中代替已移除元素的元素。直接看实例吧:
let arr =[1,2,3,4,5,6] //以下三步是独立操作,非连续操作。偷了个小懒 arr.splice(2, 2, '11','12'); //这个操作删除了 3,4,并在其位置上添加了 11,12,相当于改,其结果[1, 2, "11", "12", 5, 6] arr.splice(2,0, '11','12');//这个操作删除了 0 个元素,添加了 11,12,相当于增,其结果[1, 2, "11", "12",3,4,5,6]; arr.splice(2,2, );//这个操作删除了 2 个元素,添加了 0 个元素,相当于删,其结果[1, 2, "11", "12",3,4,5,6],相当于增加;
splice 方法的 start 参数也支持负数,其会自动类加 length,直到为正。
API 之重排序
说道数据处理,也许你利马会想到排序,什么冒泡,差值,希尔,快速排序算法。而 JS 提供了 reverse()数组反转和 sort()排序来对数组进行重排序。
数组反转 reverse():和其名字描述的一致,用于数组反转,需要注意的是,其是对数组本省的操作,并不会产生新数组;很简单,看个示例就明白了:
var color =['a','b','e','d','c','f']; color.reverse(); console.log(color);//打印[f,c,d,e,b,a]
数组排序 sort():这里所说的排序,并不是狭义的有序排列,你可以利用这个方法把有序的数组进行无序排列,为啥?应为 sort()方法支持你自己写比较函数。另外,在没有比较函数的情况下,sort()方法是根据每个数组项的 toString()后根据字典顺序进行排序的;
var color =['a','b','e','d','c','f']; color.sort(); console.log(color);//打印[a,b,c,d,e,f] var num =[1,3,2,12,24,5,7,19]; num.sort(); //这将证明上面提到的根据每个数组项的 toString()后根据字典顺序进行排序 console.log(num);//打印[1, 12, 19, 2, 24, 3, 5, 7]
如果你想让上面的数据进行升序或者降序进行排序,你需要自己写一个比较函数,即这样:
var num =[1,3,2,12,24,5,7,19]; num.sort(function(a,b){ return a-b; }); console.log(num);//打印[1, 2, 3, 5, 7, 12, 19, 24] num.sort(function () //让有序变成乱序 { return Math.random()<0.5?1:-1; }); console.log(num);//打印[3, 5, 2, 1, 7, 19, 12, 24]
API 之循环遍历
也许你已习惯了 for 循环,或者你对 jquery 的 each 方法已经产生了依赖,或许你应该接触点新知识了,毕竟 ES6 已经不算新了;ES7 已经开始被支持了;而你还不知道用 ES5 的 map,some,every,filter 来循环遍历你的数组,甚至是 forEach。
- every():对数组中的每一项运行给定函数,如果该函数对每一项都返回 true,则返回 true;
- some():对数组中的每一项运行给定函数,如果该函数任一项都返回 true,则返回 true;
所以,纵向看,其实 every 是所有项的&&操作,而 some 是||操作; - filter():对数组中的每一项运行给定函数,返回该函数该返回 true 的项组成新的数组;
- map():对数组中的每一项运行给定函数,返回该函数该返回 true 的项组成新的数组;
- forEach():功能类似于 for 循环;
对于上面 5 个方法,都有类似的回调参数(item,index,array),试着从一个例子来了解他们,一个简单的例子显得有些苍白。假如现在我们有这样一个需求,已知某个四川省某个景区今日接待旅客总人数 10000 人,然后从购票信息获取到前十名的省份和人数,我们想计算这些省份每个所占比例,并把他们的人数用一个数组单独保存下来,用来找最大值,最小值,我们试着不用 for 循环来解决这个问题;
var totle = 10000; var data = [ {name:'四川省',num:3000}, {name:'重庆市',num:500}, {name:'江西省',num:900}, {name:'湖南省',num:600}, {name:'陕西省',num:800}, {name:'河北省',num:300}, {name:'湖北省',num:400}, {name:'北京市',num:600}, {name:'云南省',num:400}, {name:'湖南省',num:300} ]; var tempArr = []; data = data.map(function (item) { tempArr.push(item.num); item.percent = item.num/totle; return item; }) console.log(tempArr); //[3000, 500, 900, 600, 800, 300, 400, 600, 400, 300] console.log(data); //0:{name: "四川省", num: 3000, percent: 0.3} 1:{name: "重庆市", num: 500, percent: 0.05}......
短短四行代码就完成了这两个需求,如果我们只想要第 2 名到第 9 名的数据(即去掉一个最高分,去掉一个最低分)拿来做分析,所以根绝前面抽离出来的数组,我们再借助 sort()和 filter()来完成这个需求:
tempArr.sort(function (v1,v2) { // 降序排列 return v2-v1; }); var max = tempArr.shift(),min = tempArr.pop(); data=data.filter(function (item) { //这个方法并没有完全达到需求,这里只是演示 filter 的用法,你可以试着优化这个函数,来完成这个需求 return (item.num!==max)&&(item.num!==min) }) console.log(tempArr); //体会一下 shift 和 pop[900, 800, 600, 600, 500, 400, 400, 300] console.log(data); //0:{name: "重庆市", num: 500, percent: 0.05} 1:{name: "江西省", num: 900, percent: 0.09}打印出来,结果只剩下 7 个了,因为最小值出现了两次。
通过上面两个例子,也许你应该已经体会到了这些原生数组 API 的作用了,他们在数据处理中,优势非常大,但也不能说,以后就可以完全不依赖 ofr 循环了,还是很难,上面五个方法有一个通病,就是无法中止遍历,即在循环中 break,break 一些遍历查找中,还是相当省时,这也是为啥有时我们还是需要 for 循环来做一些操作. 至于 some,every,foreach,你可以自己动手感受一下。
别总是沉溺于已会的那点知识,别总依赖框架,插件。用好原生 API,你的代码将显得干净,有趣。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论