你有没有遇到过如下的场景。coding中当你要处理一个数组的时候,脑海里只浮现出了forEach;面试中,当面试官让你说说数组的方法的时候,脑海里只浮现出了forEach;做梦时当一个数组追杀你,你能想到的只有用forEach来抵抗。如果是,那么你一定要听一听今年数组举办的32场演唱会。只要听完了这32场演唱会,保证下次当你遇到数组的时候,闭着眼睛都能想出32种方法对付他们。另外,友情提示,结尾有彩蛋。
书归正传,数组 是javascript中最常用的引用对象类型之一,在javascript的各种常用的数据结构中,诸如队列、栈,他们的基本实现方式用的就是数组,因此掌握数组的各项操作也就成为能否自如操控数据结构的要点。截止到ES8,数组目前共有32种方法,可大体分为如下几种:
-
数组的判断 1个方法
-
数组内容的判断 3个方法
-
数组内容的获取 4个方法
-
数组的构造 6个方法
-
数组的改造 9个方法
-
数组的循环遍历 4个方法
-
数组的累加 2个方法
-
数组转字符串 3个方法
数组的判断 1个方法
Array.isArray()
-
语法:Array.isArray(obj)
-
参数:需要检查的对象
-
返回:true,false
开唱:
判断数据类型的方法有几个比如使用typeof,instanceof,那么为什么还要有isArray方法呢?
首先说说typeof。众所周知,javascript有五个基本数据类型undefined,null,number,string,boolean,还有引用数据类型Object,Array,Function,Data,Date,RegExp等。typeof 运算符返回一个用来表示表达式数据类型的字符串,可能的字符串有:"number"、"string"、"boolean"、"object"、"function" 和 "undefined"。这里面是没有Array的,无论引用的是什么类型的对象,它都返回 "object",因此typeOf是不能用来判断数组的。那么instanceof呢?通常来讲,使用instanceof就是判断一个实例是否属于某种类型,这是基于javascript的原型继承机制的,然而在浏览器的环境中如果使用iframe,则可能出现多个不同的全局变量,从而无法发挥instanceof的作用,因此,Array.isArray()成功荣升为最靠谱的检测方法。但话说回来,其实它的的实现方式还是很简单的:
-
1 Array.isArray = function(arg) { 2 3 return Object.prototype.toString.call(arg) === '[object Array]'; 4 5 };
需要特别指出的是,Array.isArray()方法是一个静态方法,只能通过Array对象来调用。
数组内容的判断 3个方法
Array.prototype.every()
-
语法:arr.every(callback[, thisArg])
-
参数:
-
currentValue 当前值
-
currentIndex 当前值的位置
-
array 所操作的数组
-
callback 断言数组内每个元素的callback函数
-
thisArg 可选 执行callback时作为this的值
-
-
返回:t断言数组的所有元素是否满足callback函数,是则返回true,否则返回false
Array.prototype.some()
-
语法:arr.some(callback[, thisArg])
-
参数:
-
currentValue 当前值
-
currentIndex 当前值的位置
-
array 所操作的数组
-
callback 断言数组内每个元素的callback函数
-
thisArg 可选 执行callback时作为this的值
-
-
返回:断言数组内是否有元素满足callback函数,是则返回true,否则返回false
Array.prototype.includes() ECMA7
-
语法:
-
arr.includes(searchElement)
-
arr.includes(searchElement, fromIndex)
-
-
参数:
-
-
searchElement 需要查找的元素
-
fromIndex 可选 指定开始查询位置
-
-
返回:true,false
开唱:
如果你要对数组内容进行一些判断,可以先不要想着用forEach,优先思考一下上面三个是否可以满足你的需求。其中,every可以判断数组内的内容是否全部满足callback函数的条件,而some则可以判断数组内是否有内容满足callback函数的条件。 举个栗子:
-
1 function isBigEnough(element, index, array) { 2 3 return element >= 10; 4 5 } 6 7 8 9 [12, 5, 8, 130, 44].every(isBigEnough); // false 10 11 [12, 5, 8, 130, 44].some(isBigEnough); // true
而includes方法则可以查找是否具体存在某一个元素,或者从某个具体的位置开始是否存在某个元素。但值得注意的是,includes方法是ECMA7中的标准,在使用之前需要确定运行环境是否支持此项方法。
-
1 var a = [1, 2, 3]; 2 3 a.includes(2); // true 4 5 a.includes(2, 1); // true 6 7 a.includes(2, 2); // false 8 9 a.includes(4); // false
数组内容的获取 4个方法
Array.prototype.find() ECMA6
-
语法:arr.find(callback[, thisArg])
-
参数:
-
currentValue 当前值
-
currentIndex 当前值的位置
-
array 所操作的数组
-
callback 对数组内每个值执行的方法
-
thisArg 可选 执行callback时作为this的值
-
-
返回:第一个满足callback条件的值
Array.prototype.findIndex() ECMA6
-
语法:arr.findIndex(callback[, thisArg])
-
参数:
-
currentValue 当前值
-
currentIndex 当前值的位置
-
array 所操作的数组
-
callback 对数组内每个值执行的方法
-
thisArg 可选 执行callback时作为this的值
-
-
返回:满足callback条件的位置,没有则返回-1
Array.prototype.indexOf()
-
语法:arr.indexOf(searchElement[, fromIndex])
-
参数:
-
-
searchElement 需要查找的元素
-
fromIndex 可选 指定查询位置
-
-
返回:查到的第一个元素在数组内的位置,没有返回-1
Array.prototype.lastIndexOf()
-
语法:arr.lastIndexOf(searchElement[, fromIndex])
-
参数:
-
-
searchElement 需要查找的元素
-
fromIndex 可选 指定查询位置
-
-
返回:查到的最后一个元素在数组内的位置,没有返回-1
开唱:
这一组方法和数组内容判断的一组有些相似,只不过,数组内容判断的方法返回的都是true或者false,而这一组方法都会返回具体的值。 find方法会返回满足你条件的第一个元素,而findIndex会返回满足你条件的第一个元素的位置。而indexOf、lastIndexOf方法则和includes方法很相似,只不过前两个返回的是具体的位置,includes返回的是布尔值。 举个栗子:
-
1 function isBigEnough(element) { 2 3 return element >= 15; 4 5 } 6 7 8 9 console.log([12, 5, 8, 130, 5, 44].find(isBigEnough)); // 130 10 11 console.log([12, 5, 8, 130, 5, 44].findIndex(isBigEnough)); // 3 12 13 console.log([12, 5, 8, 130, 5, 44].indexOf(5)); // 1 14 15 console.log([12, 5, 8, 130, 5, 44].lastIndexOf(5)); // 4 16 17 console.log([12, 5, 8, 130, 5, 44].some(isBigEnough)); // true 18 19 console.log([12, 5, 8, 130, 5, 44].every(isBigEnough)); // false 20 21 console.log([12, 5, 8, 130, 5, 44].includes(5,4)); // true
数组的构造 6个方法
Array.from() ECMA6
-
语法:Array.from(arrayLike[, mapFn[, thisArg]])
-
参数:
-
arrayLike 具有长度属性和有序元素的对象
-
mapFn 可选 对参数arrayLike执行的map函数
-
thisArg 可选 执行mapFn时作为this的值
-
-
返回:一个新的数组实例
Array.of() ECMA6
-
语法:Array.of(element0[, element1[, ...[, elementN]]])
-
参数:elementN 生成数组的元素
-
返回:一个新的数组实例
Array.prototype.concat()
-
语法:ar newarray = oldarray.concat(value1[, value2[, ...[, valueN]]])
-
参数:valueN 搭伙到原数组中的元素或数组
-
返回:一个新的数组实例
Array.prototype.filter()
-
语法:var newArray = arr.filter(callback[, thisArg])
-
参数:
-
currentValue 当前值
-
currentIndex 当前值的位置
-
array 所操作的数组
-
callback 断言数组的每个元素是否满足callback函数,返回true则保留元素,返回false则删除元素
-
thisArg 可选 执行callback时作为this的值
-
-
返回:一个新的数组实例
Array.prototype.map()
-
语法:var newArray = arr.filter(callback[, thisArg])
-
参数:
-
currentValue 当前值
-
currentIndex 当前值的位置
-
array 所操作的数组
-
callback 遍历原数组元素生成新数组元素的函数
-
thisArg 可选 执行callback时作为this的值
-
-
返回:一个新的数组实例
Array.prototype.slice()
-
语法:
-
arr.slice()
-
arr.slice(begin)
-
arr.slice(begin, end)
-
-
参数:
-
begin 可选 浅拷贝的初始位置
-
end 可选 浅拷贝的结束位置
-
-
返回:一个新的数组实例
开唱:
在这一组中都是可以返回一个新数组,同时不会改变原数组的方法,因此把它们归为数组的构造这一类,它们都可以构造出一个新的数组。
Array.from(),Array.of()是ES6中新增的方法。需要特别指出的是,Array.from(),Array.of()方法是静态方法,只能通过Array对象来调用。
Array.from方法用于将两类对象转为真正的数组:类似数组的对象( array-like object )和可遍历( iterable )的对象(包括 ES6 新增的数据结构 Set 和Map )。在此之前,我们习惯于使用Array.prototype.slice.call()这种方式,将类数组对象转换为数组,如今我们有了更加直接的方法。
-
1 function f() { 2 3 return Array.from(arguments); 4 5 } 6 7 f(1, 2, 3); 8 9 // [1, 2, 3]
Array.of()方法则更加简单,将若干参数元素组成一个新的数组。
-
1 Array.of(1); // [1] 2 3 Array.of(1, 2, 3); // [1, 2, 3]
Array.prototype.concat()方法,既可以用于将目标数组和参数数组连接成为一个新数组,也可以实现类似于将参数元素按顺序push进数组的效果。除了使用concat,也可以使用ES6中新增的扩展运算符(spread,也就是三个点...),实现合并数组的功能。
-
1 var arr1 = ['a', 'b']; 2 3 var arr2 = ['c']; 4 5 var arr3 = ['d', 'e']; 6 7 8 9 // ES5的合并数组 10 11 arr1.concat(arr2, arr3); 12 13 // [ 'a', 'b', 'c', 'd', 'e' ] 14 15 16 17 // ES6的合并数组 18 19 [...arr1, ...arr2, ...arr3] 20 21 // [ 'a', 'b', 'c', 'd', 'e' ]
通过Array.prototype.filter()方法的命名就可以看出,它实现了一个类似于过滤器的效果。满足参数函数条件的元素将被过滤出来组成一个新的数组返回。
-
1 var myArr = [1,2,3,4,5]; 2 3 var result = myArr.filter(v=>v<3) 4 5 console.log(result) //[ 1, 2 ]
Array.prototype.map()方法有些类似于forEach方法。不同的是,forEach方法只是单纯的循环遍历数组,而map可以将循环遍历后的结果组成一个新的数组返回。
-
1 var numbers = [1, 5, 10, 15]; 2 3 var doubles = numbers.map(x => x * 2); 4 5 console.log(doubles) //[ 2, 10, 20, 30 ]
Array.prototype.slice()方法实现数组的截取功能,可以指定开始结束位置,截取相应位置内的元素组成新的数组。
-
1 var a = ['zero', 'one', 'two', 'three']; 2 3 var sliced = a.slice(1, 3); 4 5 6 7 console.log(a); // ['zero', 'one', 'two', 'three'] 8 9 console.log(sliced); // ['one', 'two']
数组的改造
Array.prototype.copyWithin() ECMA6
-
语法:
-
arr.copyWithin(target)
-
arr.copyWithin(target, start)
-
arr.copyWithin(target, start, end)
-
-
参数:
-
target 拷贝到的目标位置
-
start 可选 拷贝内容起始位置
-
end 可选 拷贝内容结束位置
-
Array.prototype.fill() ECMA6
-
语法:
-
arr.fill(value)
-
arr.fill(value, start)
-
arr.fill(value, start, end)
-
-
参数:
-
value 填充数组的值
-
start 可选 填充起始位置
-
end 可选 填充结束位置
-
Array.prototype.pop()
-
语法:arr.pop()
-
返回:移除的元素
Array.prototype.push()
-
语法:arr.push([element1[, ...[, elementN]]])
-
参数:elementN 压如数组尾部的元素
-
返回:数组的长度
Array.prototype.shift()
-
语法:arr.shift()
-
返回:移除的元素
Array.prototype.unshift()
-
语法:arr.unshift([element1[, ...[, elementN]]])
-
参数:elementN 压如数组头部的元素
-
返回:数组的长度
Array.prototype.reverse()
-
语法:arr.reverse()
-
返回:相反的数组
Array.prototype.sort()
-
语法:
-
array.sort()
-
arr.sort(compareFunction)
-
-
参数:
-
compareFunction 可选 定义排序规则的函数
-
-
返回:一个新的数组实例
Array.prototype.splice()
-
语法:
-
arr.splice(start)
-
arr.splice(start, deleteCount)
-
arr.splice(start, deleteCount, item1, item2, ...)
-
-
参数:
-
start 开始改变数组的位置
-
deleteCount 可选 删除数组的数量
-
itemN 可选 插入数组的元素
-
-
返回:包含删除元素的数组
开唱:
Array.prototype.copyWithin()和Array.prototype.fill()是ES6中新增的方法。Array.prototype.copyWithin()实现了数组内的拷贝,可以选定拷贝数据来源的开始结束位置,以及替换目标开始位置。Array.prototype.fill()则会将指定位置的数组元素全部替换为指定的值。
-
1 // 将3号位复制到0号位 2 3 [1, 2, 3, 4, 5].copyWithin(0, 3, 4) 4 5 // [4, 2, 3, 4, 5] 6 7 8 9 //从1号位开始,向原数组填充7,到2号位之前结束 10 11 ['a', 'b', 'c'].fill(7, 1, 2) 12 13 // ['a', 7, 'c']
这一组方法中,我们最熟悉的莫过于pop,push,shift,unshift这四个方法。其中pop,push是控制数组尾部元素进出的方法,而shift,unshift是控制数组头部元素进出的方法。通过上面的方法我们可以通过数组模拟实现栈和队列的效果。栈是先进后出,进栈使用push方法,出栈使用pop方法。队列是先进先出,进队列使用push方法,出队列使用shift方法。
Array.prototype.reverse()方法可得到一个反转的队列,但它在返回反转数组的同时,也改变的原数组的排列顺序,这一点是需要注意的,因此将它归入了数组的改造这一组当中。同样Array.prototype.sort()也是用于数组排序的一个方法,不传参数将根据元素的unicode进行排序,同样会改变原数组的排列顺序。
Array.prototype.splice()方法可以实现数组特定位置元素的插入,删除。
-
1 var myArr = [1,2,3,4,5]; 2 3 // 删除3号位元素 4 5 myArr.splice(2,1) //[ 1, 2, 4, 5 ] 6 7 // 在第3号位元素的位置插入元素3 8 9 myArr.splice(2,0,3) //[ 1, 2, 3, 4, 5 ] 10 11 // 删除第4号位及后面的元素 12 13 myArr.splice(3) //[ 1, 2, 3 ] 14 15 // 第4号位元素位置插入元素4,5 16 17 myArr.splice(3,0,4,5) //[ 1, 2, 3, 4, 5 ]
数组的遍历
Array.prototype.forEach()
-
语法:arr.forEach(function callback(currentValue, index, array) { //your iterator }[, thisArg]);
-
参数:
-
currentValue 当前处理的元素
-
index 当前元素的位置
-
array 当前处理的数组
-
callback 对每个元素遍历执行的方法
-
thisArg 执行callback时的this值
-
-
返回:undefined
Array.prototype.keys() ECMA6
-
语法:arr.keys()
-
返回:数组迭代器对象
Array.prototype.values() ECMA6
-
语法:arr.values()
-
返回:数组迭代器对象
Array.prototype.entries() ECMA6
-
语法:arr.entries()
-
返回:数组迭代器对象
开唱:
相信大部分人对Array.prototype.forEach()这个数组方法并不陌生,它对数组元素遍历执行回调方法,这里不做过多介绍。Array.prototype.entries(),Array.prototype.keys()和Array.prototype.values()是ES6中新增的三个数组方法,用于遍历数组。它们都返回一个遍历器对象,可以用for...of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。
-
1 for (let index of ['a', 'b'].keys()) { 2 3 console.log(index); 4 5 } 6 7 // 0 8 9 // 1 10 11 12 13 for (let elem of ['a', 'b'].values()) { 14 15 console.log(elem); 16 17 } 18 19 // 'a' 20 21 // 'b' 22 23 24 25 for (let [index, elem] of ['a', 'b'].entries()) { 26 27 console.log(index, elem); 28 29 } 30 31 // 0 "a" 32 33 // 1 "b"
数组的累加
Array.prototype.reduce()
-
语法:arr.reduce(callback[, initialValue])
-
参数:
-
accumulator 累加值
-
currentValue 当前值
-
currentIndex 当前值的位置
-
array 所操作的数组
-
callback 累加器
-
initialValue 可选 初始值
-
-
返回:累加器最终计算结果
Array.prototype.reduceRight()
-
语法:arr.reduceRight(callback[, initialValue])
-
参数:
-
accumulator 累加值
-
currentValue 当前值
-
currentIndex 当前值的位置
-
array 所操作的数组
-
callback 累加器
-
initialValue 可选 初始值
-
-
返回:累加器最终计算结果
开唱:
Array.prototype.reduce()和Array.prototype.reduceRight()方法都接收一个函数作为累加器,数组中的每个值(reduce从左到右,reduceRight从右到左)开始缩减,最终为一个值。相比与forEach、map等数组方法,reduce不必再另外定义一个变量存储最终计算结果,更加方便。另外,通过观察,不难发现一个规律,除了Array.prototype.reduce()和Array.prototype.reduceRight()方法,其他有callback回调参数的方法中,其callback回调函数中的参数都是包含三个参数的:当前值currentValue,当前值的位置currentIndex,所操作的数组array;而Array.prototype.reduce()和Array.prototype.reduceRight()方法则包含四个,多了一个累加值作为第一个参数。
-
1 var total = [0, 1, 2, 3].reduce(function(sum, value) { 2 3 return sum + value; 4 5 }, 0); 6 7 // total is 6
数组转字符串
Array.prototype.toString()
-
语法:arr.toString()
-
返回:数组的字符串表示
Array.prototype.toLocaleString()
-
语法:
-
arr.toLocaleString();
-
arr.toLocaleString(locales);
-
arr.toLocaleString(locales, options);
-
-
参数:
-
locales BCP 47语言标签字符串
-
options 可选 配置参数对象
-
-
返回:数组的字符串表示
Array.prototype.join()
-
语法:
-
arr.join()
-
arr.join(separator)
-
-
参数:separator 可选 分割符
-
返回:数组的字符串表示
开唱:
我们时常会遇到数组与字符串之间的转换。根据上面学到的知识,将字符串转换为数组,除了字符串的方法,我们还可以使用数组的Array.from()方法。而将数组转换为字符串,则可以使用上面的三个方法。其中Array.prototype.toString()方法可以将数组直接转换为字符串的表示形式。Array.prototype.toLocaleString()方法可以根据参数将数组的字符串表示本地化,但支持的情况并不是很好,因此不用过多考虑。Array.prototype.join()方法可以指定数组转换为字符串的分隔符。
-
1 var months = ['Jan', 'Feb', 'Mar', 'Apr']; 2 3 months.toString(); // "Jan,Feb,Mar,Apr" 4 5 months.join('-'); // "Jan-Feb-Mar-Apr"
那位说,听你说了这么多,也没见什么演唱会啊,那下面就让我们真正的唱一段。
判断是不是数组,isArray最靠谱。
按照条件来判断,every/some给答案
是否包含此元素,includes最快速。
find/findIndex很相似,按条件给第一个值。
indexOf/lastIndexOf也很强,有没有来在哪忙。
from和of,都能用来生数组。
concat当红娘,数组结婚她帮忙。
filter瘦身有一套,不想要的都不要。
map整容有实力,改头换面出新意。
slice就像买切糕,想切哪来就下刀。
自力更生很重要,copyWithin自己搞。
fill就像填大坑,想往哪扔往哪扔。
搬山摸金四兄弟,pop、push、shift、unshift不难记。
造反其实很容易,reverse一下看好戏。
sort排序有技巧,能小大来能大小。
splice要认识,能插能删有本事。
forEach最熟悉,有人说它是万能滴。
keys、values、entries,遍历数组新方式。
算总账,不要慌,reduce、reduceRight帮你忙。
toString,join变字符,toLocaleString不常用。
当里个当,当里个当,数组32方法,猥琐发育不要浪,嘿!不要浪!
如果你喜欢我们的文章,关注我们的公众号和我们互动吧。