资讯专栏INFORMATION COLUMN

forEach、map、filter、find、sort、some等易错点整理

AJie / 1509人阅读

摘要:最近手头上做了一个很大的后台管理项目,前端对复杂数据的处理要求颇高,也确实让自己发现了很多之前被忽视的细节。鸣人佐助卡卡西佐助佐助佐助但是很遗憾及更早版本也不支持。

  最近手头上做了一个很大的后台管理项目,前端对复杂数据的处理要求颇高,也确实让自己发现了很多之前被忽视的细节。在此特整理出来,希望不熟悉的朋友们们以后可以绕开我踩的这些坑。本文初衷在于帮助大家梳理一些数组操作上的重点和易错点,希望也能帮助和提醒大家,我会尽可能写的幽默些,加深大家的记忆。水平有限,真心无限。希望大家喜欢,请不要吝啬你们的赞,谢谢

一、常用方法解析

  说起数组操作,我们肯定第一反应就是想到forEach()、map()、filter()等方法,下面分别阐述一下各方法的优劣。

1、forEach 1.1 基础点

  forEach的使用频率很高,多用于对数组自身的改变和各元素相关统计性的计算,重要特性如下:

可以改变数组自身,没有返回值;

中途不能用常规操作跳出循环,可以用抛出异常(try/catch)的方式,但不推荐这样做;

1.2 易错点

forEach()不一定改变自身数组。我们可以看看数组中的元素是值类型和引用类型场景下,是否都能获得改变:

var arr1 = [
   {name:"鸣人",age:16},
   {name:"佐助",age:17}
];
var arr2 = [1,2,3];

arr1.forEach(item => { 
  item.age = item.age + 1}
);

//=> [{name:"鸣人",age:17},{name:"佐助",age:18}]

arr2.forEach(item => {
  item = item * 2}
)

// => [1,2,3]

最后的结果是,arr1发生了改变,鸣人、佐助都长了一岁,arr2没有任何改变。所以,可以粗暴得出结论:当数组中元素是值类型,forEach绝对不会改变数组;当是引用类型,则可以改变数组

不支持链式操作,所以以下代码是错误的:

[1,2,3,4,5].forEach(
   item => console.log(item)
).filter(item => { 
   return item > 2 
})
// Uncaught TypeError: Cannot read property "filter" of undefined

注意:这里我们说仅仅是forEach()这个方法不支持链式调用,在调用forEach之前,前面的数组你怎么玩链式都没问题,最后返回一个正常数组即可:

// 这个没问题
[1,2,3,4,5].filter(item => { 
  return item > 2 
}).forEach(item => {
   console.log(item) 
})

不会在迭代之前创建数组的副本,这个使用场景太少太少了,忽略了...

2、map

  map()功能很强大,forEach()的一些局限性它很多都能解决。"map"即"映射",也就是原数组被"映射"成对应新数组。

2.1 基础点

新建一个数组,需要有承载对象,也意味着原始数组在调用它后不会发生变化;

该数组中的每个元素都调用一个提供的函数后返回结果。

2.2 易错点

创建新数组不代表不能用它改变原有数组,你用原有数组去承载就可以:

let arr = [1,2,3];
arr = arr.map(item => { return item * 2 })

arr同样也会改变,所以这也不费事嘛。。。

map()中每个元素都要执行相应的回调函数,所以必须要有return(千万别学某些人,判断过程一复杂,忘了return,最后得到的是个空数组,哭天喊地的~~~),如果你想给数组做一定的过滤处理,那map()基本上行不通:

let newArr = [1,2,3,4,5].map(item => { if(item > 3) return item })
// => [undefined, undefined, undefined, 4, 5]

最终得到的结果是[undefined, undefined, undefined, 4, 5]。别和我说你简单处理一下就能凑合用,人生不能凑合,代码也是

3、filter

  map()没法做到的过滤,就交给filter()去完成,这个大家肯定也都知道。filter()和map()很像,就像周董《东风破》和《发如雪》一样像,也是创建一个新数组,新数组中的元素是筛选出来的符合条件的所有对象。简单写个例子:

let newArr = [1,2,3,4,5].filter(item =>{
   if(item > 3) return item 
})
//  => [4,5]

这个相信也没啥易错点,有的话欢迎评论指出~~~

4、sort()

sort()用于对数组的元素进行排序。排序顺序可以是字母或数字,并按升序或降序。

4.1 基础点

1.默认排序按字母升序(更准确一些是根据字符串Unicode码点):

[3,4,2,1,5].sort()
// => [1,2,3,4,5]

["Javascript","Vue","React","Node","Webpack"].sort();
// => ["Javascript", "Node", "React", "Vue", "Webpack"]
4.2 易错点

sort()与map()、filter()等不同,它直接改变原始数组(很重要!);

如果想按照其他标准进行排序,就需提供比较函数compareFunction(a,b),数组会按照调用该函数的返回值排序,即a和b是两个将要比较的元素:

如果compareFunction(a,b)小于0,则a排列到b之前;

如果 compareFunction(a, b)等于0,a和b的相对位置不变(并不保证);

如果 compareFunction(a, b)大于0,b排列到a之前;

直接上例子:

let Users = [
  {name:"鸣人",age:16},
  {name:"卡卡西",age:28},
  {name:"自来也",age:50},
  {name:"佐助",age:17}
];
Users.sort((a,b)=> {
   return a.age - b.age
})

// => 鸣人、佐助、卡卡西、自来也
5、some()

some()也是很好的一个方法,用于检查数组中是否有某些符合条件。

5.1 基础点

只要有一个满足即返回true,之后的不再执行(所以说对性能很友好!)。

var result = [
   {name:"鸣人",age:16},
   {name:"佐助",age:17}
].some(item => {
    return item.age > 16 
});
=> true
5.2 易错点

  some()和下面讲的every()返回的都是Boolean值,仅此而此,别多想......

6、every()

  如果说some()是"||"判断,那every()就是"&&"判断,它用于检测数组中的每一项是否都满足条件,只有都满足了才会返回true。这点也很好理解:

var result = [
   {name:"鸣人",age:16},
   {name:"佐助",age:17}
].every(item => {
    return item.age > 16 
});
=> false
二、其他经典方法

  在我们的日常工作中,会有很多业务需求是上述方法做不到的,比如下面三个需求:

给一个数组做去重处理;

判定当前数组里是否有某个元素,并返回该元素;

判定当前数组里是否有某个元素,并把它去除;

针对需求1,我相信看到"去重",你肯定会想到new Set(),这也是个经常出现的面试题;针对需求2,当你看到判定当前数组中是否有某个元素,也许会说filter() 不就是干这脏活累活的吗? 还真不是,不信,我们分别展开讨论一下吧。

1. 数组去重(没你想的那么简单) 1.1 new Set() 的局限性

  数组去重,基本上论坛上各位大神的面试题里都会有这个,没错,正是new Set(),很经典的办法,面试必备:

let tempArr = new Set([1,2,3,3,4,4,5])
// => {1,2,3,4,5} 

//并且已有元素是添加不进去的:
tempArr.add(3) 
// => {1,2,3,4,5}

tempArr.add(6)
// => {1,2,3,4,5,6}

恩,很棒,一定注意new Set()会将结果转换成对象!但实际工作中我们很少会和元素是值类型的数组打交道,那看看元素是引用类型还行不行:

let mySet = new Set();
mySet.add(1); // Set(1) {1}
mySet.add(5); // Set(2) {1, 5}
mySet.add(5); // Set(2) {1, 5} 这里体现了值的唯一性
mySet.add("some text"); 
[...mySet]
// => [1,5,"some text"]

mySet.add({name:"jay Chou",age:40});
mySet.add({name:"jay Chou",age:40});

[...mySet]
// => [1,5,"some text",{name:"jay Chou",age:40},{name:"jay Chou",age:40}]

看到了吧,Set()没法去重元素是引用对象的数组。那接下来咋整呀?

1.2 _.uniqWith()

别担心,Lodash帮我们,Lodash是一个一致性、模块化、高性能的JavaScript实用工具库。它有提供给了我们一个很好的方法——_.uniqWith():

import _ from "lodash";


//=> [{ "x": 1, "y": 2 }, { "x": 2, "y": 1 }]

其中, _.isEqual(value,other)用于执行深比较来确定两者的值是否相等。 _.uniqWith()做去重处理。

2. 获取数组中的指定元素

  在工作中我们还有一个比较常见的场景,就是在数组中找到我想要的那一个,并且返回给我。好的,some()已经办不到了,它只会告诉我是否存在,filter()确实可以做到,但是如果我本身就知道这个数组里即使有我想的那个,也肯定只有一个,不可能出现多个,所以,出于性能的考虑,我不想用filter()给我从头遍历到尾,这样怎么办?

2.1 findIndex()

  好了,既然filter()不支持中断遍历,那我们就要找一个能中断遍历的方法,我们可以使用for...of,该方法支持中断遍历,但是该方法代码量较大,不建议使用,感兴趣的同学可以查阅一下。针对这个场景,我们可以使用 findIndex()帮我们先获取到所需元素的索引值,拿到索引后,你要杀要剐随你便

let testArr = [{name:"鸣人",age:16},{name:"佐助",age:17},{name:"卡卡西",age:26}]
let index = testArr.findIndex(item => { return item.age > 16 });
// => 1

或者也可以使用Lodash提供的_.findIndex(),通过对象属性值直接获取对应索引:

let testArr = [{name:"鸣人",age:16},{name:"佐助",age:17},{name:"卡卡西",age:26}]
let index = _.findIndex(testArr, {name:"佐助"});
// => 1

注意:IE 11 及更早版本不支持findIndex() 方法。所以,如果对浏览器兼容有要求,那就用Lodash的 _.findIndex()

2.2 find()

  find()顾名思义,就是用来在数组中找到我们所需要的元素,并且和some()一样,只要有一个满足即返回该元素,不会多余遍历,对性能很友善。

let testArr = [{name:"鸣人",age:16},{name:"佐助",age:17},{name:"卡卡西",age:27},{name:"佐助",age:17}]
let result = testArr.find(item => { return item.name == "佐助"});
// => { name:"佐助",age:17 }

但是!很遗憾IE 11 及更早版本也不支持 find()

  在实际工作中,对数组的各种操作必须要做到娴熟、娴熟、再娴熟,希望我整理的这些问题都能对大家有所收获。好了,就这么多吧,后面遇到其他问题了再接着补充,希望大家喜欢!谢谢你们的赞!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/103361.html

相关文章

  • docker命令易错点整理

    摘要:拷贝目录的话,是拷贝目录里头的文件,不包括目录本身原样拷贝,不支持远程文件设置指令只能存在一个,多个的话,执行最后的那一个。设置镜像的元属性指定被其他镜像作为基础镜像时运行的操作指令指定确保服务出现任何问题而意外结束时,自动重新启动。 pause docker pause可以暂时停止容器,以释放一部分CPU出来给其他服务使用docker unpause可以解冻 docker stop ...

    YacaToy 评论0 收藏0
  • python 一些易错点整理

    摘要:与比较中与比较关键字用来在函数或其他局部作用域中使用全局变量。亲自动手试后,发现使用了只会读闭包内的变量,可以隔着多层上一哥们类比看作为步骤 这篇文章是抄抄写写得来的,纯粹是这个编辑器比笔记的好太多,才在这儿写。 函数参数传递 Python的函数参数传递 对于变量(与对象相对的概念),其实,python函数参数传递可以理解为就是变量传值操作,用C++的方式理解,就是对void*赋值。如...

    Lemon_95 评论0 收藏0
  • 数组的使用总结— (js基础复习第2期)

    摘要:前一个值,当前值,索引,数组对象产生新数组的迭代器方法类似,对数组的每个元素使用某个函数,并返回新数组和相似,传入一个返回值为布尔类型的函数。 1. 前言 数组真的是每天用了,但是有很多方法都是记不住,总是要百度查,很烦,所以才写了个数组使用总结,有什么不对的希望大家指出来。 2. 思路 先看看这些问题都记得很清楚么? 创建数组,怎么创建数组的 数组的构造方法Array有哪些方法?E...

    zhigoo 评论0 收藏0
  • 【重温基础】10.数组

    摘要:本文是重温基础系列文章的第十篇。返回一个由回调函数的返回值组成的新数组。返回一个数组迭代器对象,该迭代器会包含所有数组元素的键值对。回调函数接收三个参数,当前值当前位置和原数组。 本文是 重温基础 系列文章的第十篇。 今日感受:平安夜,多棒。 系列目录: 【复习资料】ES6/ES7/ES8/ES9资料整理(个人整理) 【重温基础】1.语法和数据类型 【重温基础】2.流程控制和错误...

    DangoSky 评论0 收藏0
  • 思维导图整理大厂面试高频数组24: 合并两个有序数组的两种双指针思想, 力扣88

    摘要:此专栏文章是对力扣上算法题目各种方法的总结和归纳整理出最重要的思路和知识重点并以思维导图形式呈现当然也会加上我对导图的详解目的是为了更方便快捷的记忆和回忆算法重点不用每次都重复看题解毕竟算法不是做了一遍就能完全记住的所 ...

    darkerXi 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<