数组应该是我们在写程序中应用到最多的数据结构了,相比于无序的对象,有序的数组帮我们在处理数据时,实在是帮了太多的忙了。今天刚好看到一篇Array.include的文章,忽然发现经过几个ES3,ES5,ES6,ES7几个版本的更迭,发现在代码中用到了好多数组的方法,所以准备全部列出来,也是给自己加深印象
1 ES3中的数组方法
- ES3兼容现在所有主流浏览器
ES3中的方法毫无疑问大家已经烂熟在心了,不过中间有些细节可以回顾加深一下记忆,比如是否修改原数组返回新数组,执行方法之后的返回值是什么,某些参数的意义是否搞混等等。熟悉的的可以直接快速浏览或者跳过。
1.1 join()方法
Array.join()方法是将一个数组里面的所有元素转换成字符串,然后再将他们连接起来返回一个新数组。可以传入一个可选的字符串来分隔结果字符串中的所有元素。如果没有指定分隔字符串,就默认使用逗号分隔。
方法Array.join()恰好与String.split()相反,后者是通过将一个字符串分隔成几个元素来创建数组
1.2 reverse()方法
Array.reverse()方法将颠倒数组中元素的顺序并返回一个颠倒后的数组。它在原数组上执行这一操作,所以说并不是创建了一个新数组,而是在已存在的数组中对元素进行重排。
1.3 sort()方法
Array.sort()是在原数组上进行排序,返回排序后的数组。如果调用方法时不传入参数,那么它将按照字母顺序对数组元素进行排序,说得更精确点,是按照字符编码的顺序进行排序。要实现这一点,首先应把数组的元素都转换成字符串(如有必要),以便进行比较。
如果数组中有未定义的元素,这些元素将放在数组的末尾
仔细看可以发现,上面顺序并没有按照数字大小进行排序。如果想按照其他标准进行排序,就需要提供比较函数。该函数比较前后两个值,然后返回一个用于说明这两个值的相对顺序的数字。比较函数应该具有两个参数 a 和 b,其返回值如下:
- 若 a 小于 b,在排序后的数组中 a 应该出现在 b 之前,则返回一个小于 0 的值。
- 若 a 等于 b,则返回 0。
- 若 a 大于 b,在排序后的数组中 a 应该出现在 b 之后,则返回一个大于 0 的值。12let a = [1,12,23,14,,undefined,null,NaN,56,6,7,"a",{},[]];a.sort((a,b) => {return a - b}); //[null, Array(0), NaN, Object, 1, 6, 7, 12, 14, 23, 56, "a",undefined, undefined × 1]
1.4 concat()方法
Array.concat() 方法用于连接两个或多个参数(数组,字符串等),该方法不会改变现有的数组,而会返回连接多个参数的一个新数组。如果传入的参数是数组,那么它将被展开,将元素添加到返回的数组中。但要注意,concat并不能递归的展开一个元素为数组的参数。
|
|
1.5 slice()方法
Array.slice() 方法可从已有的数组中返回指定的一个片段(slice),或者说是子数组。它是从原数组中截取了一个片段,并返回到了一个新数组。
Array.slice(a,b) 它有两个参数a,b
参数 | 描述 |
---|---|
a | 必选。规定从何处开始选取。如果是负数,那么它规定从数组尾部开始算起的位置。也就是说,-1 指最后一个元素,-2 指倒数第二个元素,以此类推。 |
b | 可选。规定从何处结束选取。该参数是数组片断结束处的数组下标。如果没有指定该参数,那么切分的数组包含从 start 到数组结束的所有元素。如果这个参数是负数,那么它规定的是从数组尾部开始算起的元素。 |
|
|
请注意,该方法并不会修改数组,而是返回一个新的子数组。如果想删除数组中的一段元素,应该使用下面这个方法 Array.splice()。
1.6 splice()方法
Array.splice() 方法从数组中添加/删除元素,然后返回被删除的元素。它在原数组上修改数组,并不像slice和concat那样创建新数组。注意,虽然splice和slice名字非常相似,但是执行的却是完全不同的操作。
参数 | 描述 |
---|---|
index | 必选,整数。规定添加/删除项目的位置,使用负数可从数组结尾处倒着寻找位置。 |
howmany | 可选,整数。要删除的元素数量。如果设置为 0,则不会删除元素。如果没有选择,则默认从index开始到数组结束的所有元素 |
item1, …, itemX | 可选。向数组添加新的元素。 |
|
|
大家要记住slice()和splice()两个方法第二个参数代表的意义是不一样的。虽然这很基础,可是有时候还是会弄混。
1.7 push()和pop()方法
Array.push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度。
Array.pop()方法用于删除并返回数组的最后一个元素。如果数组已经为空,则 pop() 不改变数组,并返回 undefined 值。
上面两个方法都是直接对原数组进行操作。通过上面两个方法可以实现一个先进后出的栈。
1.8 unshift和shift()方法
unshift,shift()的方法行为和push(),pop()非常相似,只不过他们是对数组的头部元素进行插入和删除。
Array.unshift() 方法可向数组的头部添加一个或多个元素,并返回新的长度。
Array.shift()方法用于删除并返回数组的第一个元素。如果数组已经为空,则 pop() 不改变数组,并返回 undefined 值。
1.9 toString()和toLocaleString()方法
和所有javascript的对象一样,数组也有toString()方法,这个方法可以将数组的每一个元素转化成字符串(如果必要的话,就调用元素的toString()方法),然后输出字符串的列表,字符串之间用逗号隔开。(用我的话来理解,其实就是遍历数组元素调用每个元素自身的toString()方法,然后用逗号连接)
toString()的返回值和没有参数的join()方法返回的字符串相同
注意,输出的结果中,返回的数组值周围没有括号。
toLocaleString方法是toString()方法的本地化版本。它是使用地区特定的分隔符把生成的字符串连接起来,形成一个字符串。
虽然是两个方法,但是一般元素两个方法的输出结果却基本是一样的,去网上找了相关文章,发现只有两种情况比较有区分,一个是时间,一个是4位数字以上的数字,举例如下
好吧,这个api和数组关系不大。。。主要还是和数组中元素自身有关。啊哈哈,尴尬。
1.10 valueOf()
Array.valueOf()方法在日常中用的比较少,该方法继承与Object。javascript中许多内置对象都针对自身重写了该方法,数组Array.valueOf()直接返回自身。
好啦,关于ES3的方法就不详细描述了,我相信大家基本上都已经完全是烂熟于心的那种,唯一可能需要加强记忆的就是一些参数含义,返回数据这些了。
2 ES5中的数组方法
- ES5中的数组方法在各大浏览器的兼容性
- Opera 11+
- Firefox 3.6+
- Safari 5+
- Chrome 8+
- Internet Explorer 9+
2.Array在ES5新增的方法中接受两个参数,第一个参数都是function类型,必选,默认有传参,这些参数分别是:
- currentValue : 数组当前项的值
- index : 数组当前项的索引
- array : 数组对象本身
第二个参数是当执行回调函数时指向的this(参考对象),不提供默认为window,严格模式下为undefined。
以forEach举例
语法
例子:
3.ES5中的所有关于遍历的方法按升序为数组中含有效值的每一项执行一次callback函数,那些已删除(使用delete方法等情况)或者未初始化的项将被跳过(但不包括那些值为 undefined 的项)(例如在稀疏数组上)。
例子:数组哪些项被跳过了
好了,上面3点基本上是ES5中所有方法的共性,下面就不重复述说了。开始正文解析每个方法的不同了
2.1 forEach()
Array.forEach() 为每个数组元素执行callback函数;不像map() 或者reduce() ,它总是返回 undefined值,并且不可链式调用。典型用例是在一个链的最后执行副作用。
注意: 没有办法中止或者跳出 forEach 循环,除了抛出一个异常。如果你需要跳出函数,推荐使用Array.some。如果可以,新方法 find() 或者findIndex() 也可被用于真值测试的提早终止。
如果数组在迭代时被修改了
下面的例子输出”one”, “two”, “three”。当到达包含值”two”的项时,整个数组添加了一个项在第一位,这导致所有的元素下移一个位置。此时在下次执行回调中,因为元素 “two”符合条件,结果一直增加元素,直到遍历次数完毕。forEach()不会在迭代之前创建数组的副本。
看完例子可以发现,使用 forEach 方法处理数组时,数组元素的范围是在callback方法第一次调用之前就已经确定了。在 forEach 方法执行的过程中:原数组中新增加的元素将不会被 callback 访问到;若已经存在的元素被改变或删除了,则它们的传递到 callback 的值是 forEach 方法遍历到它们的那一个索引时的值。
2.2 map()
Array.map 方法会给原数组中的每个元素都按顺序调用一次callback函数。callback每次执行后的返回值(没有指定返回值则返回undefined)组合起来形成一个新数组。
例子:返回每个元素的平方根的数组
2.3 filter()
Array.filter()为数组中的每个元素调用一次 callback 函数,并利用所有使得 callback 返回 true 或 等价于 true 的值 的元素创建一个新数组。那些没有通过 callback 测试的元素会被跳过,不会被包含在新数组中
例子:数组去重
2.4 some()
Array.some 为数组中的每一个元素执行一次 callback 函数,直到找到一个使得 callback 返回一个“真值”(即可转换为布尔值 true 的值)。如果找到了这样一个值,some 将会立即返回 true。否则,some 返回 false。callback 只会在那些”有值“的索引上被调用,不会在那些被删除或从来未被赋值的索引上调用。
例子:查看数组内是否含有大于0的元素
some方法可以理解成拥有跳出功能的forEach()函数,可以用在在一些需要中断函数的地方
2.5 every()
Array.every() 方法为数组中的每个元素执行一次 callback 函数,直到它找到一个使 callback 返回 false(表示可转换为布尔值 false 的值)的元素。如果发现了一个这样的元素,every 方法将会立即返回 false。否则,callback 为每一个元素返回 true,every 就会返回 true。callback 只会为那些已经被赋值的索引调用。不会为那些被删除或从来没被赋值的索引调用。
例子:检测所有数组元素的大小,是否都大于0
2.6 indexOf()
Array.indexOf()使用严格相等(strict equality,即===)进行判断searchElement与数组中包含的元素之间的关系。
Array.indexOf()提供了两个参数,第一个searchElement代表要查询的元素,第二个代表fromIndex表示从哪个下标开始查找,默认为0。
语法
Array.indexOf()会返回首个被找到的元素在数组中的索引位置; 若没有找到则返回 -1
例子:
2.7 lastIndexOf()
Array.lastIndexOf()就不细说了,其实从名字大家也可以看出来,indexOf是正向顺序查找,lastIndexOf是反向从尾部开始查找,但是返回的索引下标仍然是正向的顺序索引
。
语法
需要注意的是,只是查找的方向相反,fromIndex和返回的索引都是正向顺序的,千万不要搞混了(感觉我这么一说,大家可能搞混了,捂脸)。
例子:各种情况下的的indexOf
2.8 reduce()
Array.reduce() 为数组中的每一个元素依次执行回调函数,最后返回一个函数累计处理的结果。
语法
reduce的回调函数中的参数与前面的不同,多了第一个参数,是上一次的返回值
- accumulator : 上一次调用回调返回的值,或者是提供的初始值(initialValue)
- currentValue : 数组当前项的值
- currentIndex : 数据当前项的索引。第一次遍历时,如果提供了 initialValue ,从0开始;否则从1开始
- array : 调用 reduce 的数组
- initialValue : 可选项,其值用于第一次调用 callback 的第一个参数。如果没有设置初始值,则将数组中的第一个元素作为初始值。空数组调用reduce时没有设置初始值将会报错。
例子:数组求和
对了,当回调函数第一次执行时,accumulator 和 currentValue 的取值有两种情况:
- 调用 reduce 时提供initialValue,accumulator 取值为 initialValue ,currentValue 取数组中的第一个值;
- 没有提供 initialValue ,accumulator 取数组中的第一个值,currentValue 取数组中的第二个值。
例子:reduce数组去重
注意 :如果数组为空并且没有提供initialValue, 会抛出TypeError 。如果数组仅有一个元素并且没有提供initialValue, 或者有提供initialValue但是数组为空,那么此唯一值将被返回并且callback不会被执行。
2.9 reduceRight()方法
Array.reduceRight() 为数组中的每一个元素依次执行回调函数,方向相反,从右到左,最后返回一个函数累计处理的结果。
因为这个方法和reduce方法基本是一模一样的,除了方法相反,所以就不详细的再写一遍了
2.10 isArray()方法
之所以将这个方法放在最后,是因为这个方法和前面的不太一致,是用于确定传递的值是否是一个 Array,使用方法也很简单
例子
不过感觉除非是临时判断,不然一般也不会用这个方法去判断,一般还是下面这种万金油型的吧。
|
|
好啦,关于ES5的方法基本上就讲到这里了,感觉自己在深入去看了一些文章之后,还是有一些额外的收获的。比如对reduce这个平时不常用的方法了解更加深刻了,感觉之前很多遍历收集数据的场景其实用reduce更加方便。
3 ES6中的数组方法
不同于es5主要以遍历方法为主,es6的方法是各式各样的,不过必须要说一句,在性能上,es6的效率基本上是最低的。
3.1 …方法——concat方法的增强
英文名字叫做Spread syntax,中文名字叫做扩展运算符。
3.2 of()方法
Array.of()方法可以将传入参数以顺序的方式返回成一个新数组的元素。
其实,刚看到这个api和他的用途,还是比较懵逼的,因为看上去这个方法就是直接将传入的参数变成一个数组之外,就没有任何区别了,那么我为什么不直接用以前的写法去实现类似的效果呢,比如 let = [1,2,3];而且看上去也更加直接。然后我去翻了下最新的ECMAScript草案,其中有这么一句话
The of function is an intentionally generic factory method; it does not require that its this value be the Array constructor. Therefore it can be transferred to or inherited by other constructors that may be called with a single numeric argument.
自己理解了一下,其实大概意思就是说为了弥补Array构造函数传入单个函数的不足,所以出了一个of这个更加通用的方法,举个例子
大家可以注意到传入一个参数和传入两个参数的结果,完全是不一样的,这就很尴尬了。而为了避免这种尴尬,es6则出了一种通用的of方法,不管你传入了几个参数,都是一种相同类型的输出结果。
不过我好奇的是,如果只传入几个参数,为什么不直接let a = [1,2,3];效率和直观性也更加的高。如果要创建一个长度的数组,我肯定还是选let a = new Array(10000),这种形式,实在没有感觉到Array.of的实用场景,希望大家可以给我点指导。
3.2 from()方法
Array.from()方法从一个类似数组(拥有一个 length 属性和若干索引属性的任意对象)或可迭代的对象(String, Array, Map, Set和 Generator)中创建一个新的数组实例。
我们先查看Array.from()的语法
语法
从语法中,我们可以看出Array.from()最基本的功能是将一个类数组的对象转化成数组,然后通过第二个和第三个参数可以对转化成功后的数组再次执行一次遍历数据map方法,也就是Array.from(obj).map(mapFn, thisArg)。
对了额外说一句,这个方法的性能很差,和直接的for循环的性能对比了一下,差了百倍不止。
例子 :将一串数字字符串转化为数组
3.4 copyWithin()方法
Array.copyWithin方法,在当前数组内部,将指定位置的成员浅复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组。
这个方法有点复杂,光看描述可能大家未必能轻易理解,大家可以先看下语法,再看demo配合理解,而且自己没有想到这个方法合适的应用场景。网上也没又看到相关使用场景。但是讲道理,这个方法设计出来,肯定是经过深思熟虑的,如果大家有想到,欢迎评论给我,谢谢。
语法
例子
第一个是常规的例子,大家可以对比看第二个可以发现,这个方法是先浅复制了数组一部分暂时存储起来,然后再从目标索引处开始一个个覆盖后面的元素,直到这段复制的数组片段全部粘贴完。
再看第三个例子,可以发现当复制的数据片段从目标索引开始粘贴时,如果超过了长度,它将停止粘贴,这说明它不会改变数据的 length,但是会改变数据本身的内容。
Array.copyWithin可以理解成复制以及粘贴序列这两者是为一体的操作;即使复制和粘贴区域重叠,粘贴的序列也会有拷贝来的值。
3.5 find() 和 findIndex()方法
Array.find()方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。
Array.findIndex() 方法返回数组中满足提供的测试函数的第一个元素的值的索引。否则返回 -1。
这两个方法其实使用非常相似,使用场景有点像ES5中Array.some,都是在找到第一个满足条件的时候,跳出循环,区别的是,三种返回的值完全不一样,我想这也许是为什么要在ES6中增加这两个API的原因吧,可以理解成是数组的方法的补足。
例子:三个方法各自的返回值
注意:find()和findIndex()方法无法判断NaN,可以说是内部用 ===判断,不同于ES7中的include方法。不过这个判断方式是另外一个话题,不在本文详述了,感兴趣的同学可以去查一下。
其实还可以发现,Array.find() 方法只是返回第一个符合条件的元素,它的增强版是es5中Array.filter()方法,返回所有符合条件的元素到一个新数组中。可以说是当用find方法时考虑跟多的是跳出吧。
我感觉这4个方法配合相应的回调函数基本上可以完全覆盖大多数需要数组判断的场景了,大家觉得呢?
3.5 fill方法
Array.fill()方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素,返回原数组
这个方法的使用也非常简单,大家基本上看个语法和demo就能懂了。需要注意的是,这个方法是返回数组本身,还有一点就是,类数组不能调用这个方法,刚刚自己去改了MDN上面的文档。
语法
例子
个人感觉这个方法初始化数组挺有用的,自己一周每次测试数据时,只要new Array().fill(1);,比以前遍历直观方便多了
3.6 entries(),keys(),values()方法
Array.entries()将数组转化成一个中包含每个索引的键/值对的Array Iterator对象
Array.keys()将数组转化成一个中包含每个索引的键的Array Iterator对象
Array.values()将数组转化成一个中包含每个索引的值的Array Iterator对象。
Array.values()方法chrome浏览器并不支持,
之所以将这三个方法放在一起是有原因的额,大家可以看这三个方法其实都是一个数组转化为一种新的数据类型——返回新的Array Iterator对象,唯一区别的是转化之后的元素不一样。跟他们的名字一样,entries()方法转化为全部的键值对,key()方法转化为键,value()保留值。
例子:观察各个迭代器遍历输出的东西
Array.entries()
Array.keys()
Array.values()
关于迭代器这个东西,自己说不上什么,因为自己没有亲自用过,如果大家有什么见解课可以评论给我,我来补充和学习一下
4 ES7中的数组方法
4.1 includes()方法
Array.includes方法返回一个布尔值,表示某个数组是否包含给定的值,如果包含,则返回true,否则返回false,与字符串的includes方法类似。
这个方法大家可以看作是ES5中Array.indexOf的语义增强版,“includes”这个是否包含的意思,直接返回Boolean值,比起原来的indexOf是否大于-1,显得更加直观,我就是判断有没有包含哪个值
语法,使用方法和indexof一模一样
例子
方法还真是tmd多啊,感觉基本上应该是更新完了,前后两星期花了我4天时间吧,还是挺累的。不过收货还是很多,比如知道了ES5的方法基本上都有第二个this指向的参数,重新认识了reduce方法,感觉自己之前很多场景用reduce更好,重新熟悉了一些ES6的方法可以试用有些场景
如果能看到最后的,感觉你也是够累的,哈哈哈。
既然这么累,点颗星吧