用好 JS 原生 API 系列之数组

发布于 2023-09-08 12:17:49 字数 6957 浏览 31 评论 0

最近工作做数据交互展示,常和数据打交道,而随之而来的就是遇见后端传来的各种各样的数组,我需要用各式各样的方法来变换这些数据,来最好的展示这些数据;很多东西久了没用就容易忘,自己也是边查边用,这篇文章算是自己这一周学习的知识的总结。当然你也可以打看 MSDN 查看更标准的叙述

数组原生的 API

那些需要知道的特性:

  1. 数组在 JS 中是对象的一种,所以数组也是引用类型,所以操作时要小心,时刻记住你操作的不是一个普通类型;
  2. 每个数组都自带一个 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

文章
评论
27 人气
更多

推荐作者

櫻之舞

文章 0 评论 0

弥枳

文章 0 评论 0

m2429

文章 0 评论 0

野却迷人

文章 0 评论 0

我怀念的。

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文