你有没有遇到过如下的场景。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()成功荣升为最靠谱的检测方法。但话说回来,其实它的的实现方式还是很简单的:
Array.isArray = function(arg) {
return Object.prototype.toString.call(arg) === '[object Array]';
};
需要特别指出的是,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函数的条件。 举个栗子:
function isBigEnough(element, index, array) {
return element >= 10;
}
[12, 5, 8, 130, 44].every(isBigEnough); // false
[12, 5, 8, 130, 44].some(isBigEnough); // true
而includes方法则可以查找是否具体存在某一个元素,或者从某个具体的位置开始是否存在某个元素。但值得注意的是,includes方法是ECMA7中的标准,在使用之前需要确定运行环境是否支持此项方法。
var a = [1, 2, 3];
a.includes(2); // true
a.includes(2, 1); // true
a.includes(2, 2); // false
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返回的是布尔值。 举个栗子:
function isBigEnough(element) {
return element >= 15;
}
console.log([12, 5, 8, 130, 5, 44].find(isBigEnough)); // 130
console.log([12, 5, 8, 130, 5, 44].findIndex(isBigEnough)); // 3
console.log([12, 5, 8, 130, 5, 44].indexOf(5)); // 1
console.log([12, 5, 8, 130, 5, 44].lastIndexOf(5)); // 4
console.log([12, 5, 8, 130, 5, 44].some(isBigEnough)); // true
console.log([12, 5, 8, 130, 5, 44].every(isBigEnough)); // false
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()这种方式,将类数组对象转换为数组,如今我们有了更加直接的方法。
function f() {
return Array.from(arguments);
}
f(1, 2, 3);
// [1, 2, 3]
Array.of()方法则更加简单,将若干参数元素组成一个新的数组。
Array.of(1); // [1]
Array.of(1, 2, 3); // [1, 2, 3]
Array.prototype.concat()方法,既可以用于将目标数组和参数数组连接成为一个新数组,也可以实现类似于将参数元素按顺序push进数组的效果。除了使用concat,也可以使用ES6中新增的扩展运算符(spread,也就是三个点...),实现合并数组的功能。
var arr1 = ['a', 'b'];
var arr2 = ['c'];
var arr3 = ['d', 'e'];
// ES5的合并数组
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]
// ES6的合并数组
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]
通过Array.prototype.filter()方法的命名就可以看出,它实现了一个类似于过滤器的效果。满足参数函数条件的元素将被过滤出来组成一个新的数组返回。
var myArr = [1,2,3,4,5];
var result = myArr.filter(v=>v<3)
console.log(result) //[ 1, 2 ]
Array.prototype.map()方法有些类似于forEach方法。不同的是,forEach方法只是单纯的循环遍历数组,而map可以将循环遍历后的结果组成一个新的数组返回。
var numbers = [1, 5, 10, 15];
var doubles = numbers.map(x => x * 2);
console.log(doubles) //[ 2, 10, 20, 30 ]
Array.prototype.slice()方法实现数组的截取功能,可以指定开始结束位置,截取相应位置内的元素组成新的数组。
var a = ['zero', 'one', 'two', 'three'];
var sliced = a.slice(1, 3);
console.log(a); // ['zero', 'one', 'two', 'three']
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()则会将指定位置的数组元素全部替换为指定的值。
// 将3号位复制到0号位
[1, 2, 3, 4, 5].copyWithin(0, 3, 4)
// [4, 2, 3, 4, 5]
//从1号位开始,向原数组填充7,到2号位之前结束
['a', 'b', 'c'].fill(7, 1, 2)
// ['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()方法可以实现数组特定位置元素的插入,删除。
var myArr = [1,2,3,4,5];
// 删除3号位元素
myArr.splice(2,1) //[ 1, 2, 4, 5 ]
// 在第3号位元素的位置插入元素3
myArr.splice(2,0,3) //[ 1, 2, 3, 4, 5 ]
// 删除第4号位及后面的元素
myArr.splice(3) //[ 1, 2, 3 ]
// 第4号位元素位置插入元素4,5
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()是对键值对的遍历。
for (let index of ['a', 'b'].keys()) {
console.log(index);
}
// 0
// 1
for (let elem of ['a', 'b'].values()) {
console.log(elem);
}
// 'a'
// 'b'
for (let [index, elem] of ['a', 'b'].entries()) {
console.log(index, elem);
}
// 0 "a"
// 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()方法则包含四个,多了一个累加值作为第一个参数。
var total = [0, 1, 2, 3].reduce(function(sum, value) {
return sum + value;
}, 0);
// 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()方法可以指定数组转换为字符串的分隔符。
var months = ['Jan', 'Feb', 'Mar', 'Apr'];
months.toString(); // "Jan,Feb,Mar,Apr"
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方法,猥琐发育不要浪,嘿!不要浪!