这篇文章介绍ES6数组新增的一些常用方法。
Array.from()
方法用于将两类对象转为真正的数组:类数组对象和可遍历(iterable)的对象
(包括 ES6 新增的数据结构 Set 和 Map)。
下面是一个类似数组的对象,Array.from()
将它转为真正的数组。
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
// ES5 的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']
// ES6 的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
实际应用中,常见的类似数组的对象是 获取DOM 节点的 NodeList 集合或HTMLCollection集合,以及函数内部的arguments
对象。Array.from()
都可以将它们转为真正的数组。
看以下的案例:
<div class="container">
<div class="item">安柏</div>
<div class="item">优菈</div>
<div class="item">神里绫华</div>
</div>
// HTMLCollection 伪数组
const itemList = document.getElementsByClassName("item") // HTMLCollection(3) [div.item, div.item, div.item]
// NodeList 伪数组
const itemList2 = document.querySelectorAll(".item") //NodeList(3) [div.item, div.item, div.item]
// 1,直接进行遍历会报错 因为不是一个真正的数组,所以不能使用forEach,map,filter等数组遍历的方法
itemList.map((item)=>{console.log(item);}) // 报错信息: itemList.map is not a function
// 2,使用Array.from转为真数组
const newItemList = Array.from(itemList)
// 3,可以遍历成功
newItemList.map((item)=>{console.log(item);})
// 遍历结果
<div class="item">安柏</div>
<div class="item">优菈</div>
<div class="item">神里绫华</div>
上面代码中,querySelectorAll()
和getElementsByClassName
方法返回的是一个类数对象数组(也叫伪数组)可以将这个对象转为真正的数组,再使用map()等数组的方法。
注意:
只要是部署了 Iterator 接口的数据结构,Array.from()
都能将其转为数组。
Array.from("Eula-优菈"); // ['E', 'u', 'l', 'a', '-', '优', '菈']
let mySet = new Set(["Eula", "优菈"]);
Array.from(mySet); //['Eula', '优菈']
上面代码中,字符串和 Set 结构都具有 Iterator 接口,因此可以被Array.from()
转为真正的数组。
-----(对Iterator 不了解的可以见这篇文章)------
如果参数是一个真正的数组,Array.from()
会返回一个一模一样的新数组。
Array.from([1, 2, 3])
// [1, 2, 3]
扩展运算符(...
)也可以将某些数据结构转为数组;下面的案例是把函数传入的不定参数
进行累加计算并返回。
function sum() {
let sum = 0;
const args = [...arguments];
// [...arguments] 已经转为真正的数组 [1, 2, 3, 4] 然后累加求和
args.forEach((item) => {
return (sum += item);
});
return sum;
}
let mySum = sum(1, 2, 3, 4);
console.log("mySum:", mySum); // 10
扩展运算符背后调用的是遍历器接口(Symbol.iterator
),如果一个对象没有部署这个接口,就无法转换。
Array.from()
还可以接受一个函数作为第二个参数,作用类似于数组的map()
方法,用来对每个元素进行处理,将处理后的值放入返回的数组。
let arrayLike = [1, 2, 3, 4, 5];
let newArray1 = Array.from(arrayLike, (item) => item * 2);
console.log("newArray1:", newArray1); // [2, 4, 6, 8, 10]
// 等同于
let newArray2 = Array.from(arrayLike).map((item) => item * 2);
console.log("newArray2:", newArray2); // [2, 4, 6, 8, 10]
Array.from()
可以将各种值转为真正的数组,并且还提供map
功能。这实际上意味着,只要有一个原始的数据结构,你就可以先对它的值进行处理,然后转成规范的数组结构,进而就可以使用数量众多的数组方法。
Array.of()方法
将一组值转换为数组。
Array.of(1, 2, 3, 4); // [1, 2, 3, 4]
Array.of(3); // [3]
Array.of(3).length; // 1
// 字符也可以
Array.of("1",2,"3",4) // ['1', 2, '3', 4]
Array.of("1",2,"3",4).length // 4
这个方法的主要目的,是弥补数组构造函数Array()
的不足。因为参数个数的不同,会导致Array()
的行为有差异。如下:
console.log(new Array()); // []
console.log(new Array(3)); // [empty × 3]
console.log(new Array(3,11,8)); // [3, 11, 8]
上面代码中,new Array()
方法没有参数、一个参数、三个参数时,返回的结果都不一样。只有当参数个数不少于 2 个时,new Array()
才会返回由参数组成的新数组。
参数只有一个正整数时,实际上是指定数组的长度。
Array.of()
基本上可以用来替代Array()
或new Array()
,并且不存在由于参数不同而导致的重载。它的行为非常统一。
Array.of() // []
Array.of(undefined) // [undefined]
Array.of(1) // [1]
Array.of(1, 2) // [1, 2]
Array.of()
总是返回参数值组成的数组。如果没有参数,就返回一个空数组。
Array.of()
方法可以用下面的代码模拟实现。
function ArrayOf(){
return [].slice.call(arguments);
}
find():
数组实例的find()
方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true
的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined
。
[1, 4, -5, 10].find((n) => n < 0)
// -5
上面代码找出数组中第一个小于 0 的成员。
[1, 5, 10, 15].find(function(value, index, arr) {
return value > 9;
}) // 10
上面代码中,find()
方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组。
findIndex:
数组实例的findIndex()
方法的用法与find()
方法非常类似,返回第一个符合条件的数组成员的位置(索引),如果所有成员都不符合条件,则返回-1
。
[1, 5, 10, 15].findIndex(function(value, index, arr) {
return value > 9;
}) // 2
这两个方法都可以接受第二个参数,用来绑定回调函数的this
对象。
function f(v){
return v > this.age;
}
let person = {name: 'John', age: 20};
[10, 12, 26, 15].find(f, person); // 26
上面的代码中,find()
函数接收了第二个参数person
对象,回调函数中的this
对象指向person
对象。
find()
和findIndex()
都是从数组的0号位,依次向后检查。
findLast()和findLastIndex():
findLast()
和findLastIndex()
却是从数组的最后一个成员开始,依次向前检查,其他都保持不变。
const array = [
{ value: 1 },
{ value: 2 },
{ value: 3 },
{ value: 4 }
];
array.findLast(n => n.value % 2 === 1); // { value: 3 }
array.findLastIndex(n => n.value % 2 === 1); // 2
上面示例中,findLast()
和findLastIndex()
从数组结尾开始,寻找第一个value
属性为奇数的成员。结果,该成员是{ value: 3 }
,位置是2号位。
fill
方法使用给定值,填充一个数组。
['a', 'b', 'c'].fill(7)
// [7, 7, 7]
new Array(3).fill(7)
// [7, 7, 7]
上面代码表明,fill
方法用于空数组的初始化非常方便。数组中已有的元素,会被全部抹去。
fill
方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置。
['a', 'b', 'c'].fill(7, 1, 2)
// ['a', 7, 'c']
上面代码表示,fill
方法从 1 号位开始,向原数组填充 7,到 2 号位之前结束。
注意,如果填充的类型为对象,那么被赋值的是同一个内存地址的对象,而不是深拷贝对象。
let arr = new Array(3).fill({name: "Amber"});
arr[0].name = "Eula"; // 只改了第一个, 但是后面的所有全都改变了
console.log(arr);
// [{name: "Eula"}, {name: "Eula"}, {name: "Eula"}]
ES6 提供三个新的方法:entries()
,keys()
和values()
—用于遍历数组。
它们都返回一个遍历器对象(Iterator),可以用for...of
循环进行遍历,唯一的区别是keys()
是对键名的遍历、values()
是对键值的遍历,entries()
是对键值对的遍历。
1,keys()是对键名的遍历
let obj = {
Amber: "安柏",
Eula: "优菈",
KamisatoAyaka: "神里绫华"
};
// for of不支持遍历普通对象,可通过与Object.keys()搭配使用遍历
for (let key of Object.keys(obj)) {
console.log(key); // Amber,Eula,KamisatoAyaka 拿到的都是对象的键名
}
console.log(Object.keys(obj)); //(3) ['Amber', 'Eula', 'KamisatoAyaka']
2,values()是对键值的遍历
let obj = {
Amber: "安柏",
Eula: "优菈",
KamisatoAyaka: "神里绫华"
};
for (let key of Object.values(obj)) {
console.log(key); // 安柏,优菈,神里绫华 拿到的都是对象的值
}
console.log(Object.values(obj)); //(3) ['安柏', '优菈', '神里绫华']
3,entries()是对键值对的遍历
let obj = {
Amber: "安柏",
Eula: "优菈",
KamisatoAyaka: "神里绫华"
};
for (let key of Object.entries(obj)) {
console.log(key);
// ['Amber', '安柏']
// ['Eula', '优菈']
// ['KamisatoAyaka', '神里绫华']
}
console.log(Object.entries(obj));
// 会以一个数组重新包装起来
// [
// ["Amber", "安柏"],
// ["Eula", "优菈"],
// ["KamisatoAyaka", "神里绫华"]
// ];
entries方法还有个用法就是:将Object
转换为Map
,new Map()
构造函数接受一个可迭代的entries
。借助Object.entries
方法你可以很容易的将Object
转换为Map
:
let obj = {
name: "Eula",
age: 18
};
let map = new Map(Object.entries(obj));
console.log(map); // Map(2) {'name' => 'Eula', 'age' => 18}
includes 可以判断一个数组中是否包含某一个元素,并返回true 或者false
;
const inc = ["a", "b", "c"].includes("a");
console.log("inc:", inc); // true
该方法的第二个参数表示搜索的起始位置,默认为0
。如果第二个参数为负数,则表示倒数的位置,如果这时它大于数组长度(比如第二个参数为-4
,但数组长度为3
),则会重置为从0
开始。
[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true
没有该方法之前,我们通常使用数组的indexOf
方法,检查是否包含某个值。
if (arr.indexOf(el) !== -1) {
// ...
}
indexOf
方法有两个缺点,一是不够语义化,它的含义是找到参数值的第一个出现位置,所以要去比较是否不等于-1
,表达起来不够直观。二是,它内部使用严格相等运算符(===
)进行判断,这会导致对NaN
的误判。
[NaN].indexOf(NaN)
// -1
includes
使用的是不一样的判断算法,就没有这个问题。
[NaN].includes(NaN)
// true
数组扁平化方法 Array.prototype.flat()
也叫数组拉平、数组降维。
看下面案例:
const arr = [1, ["a", "b"], [2, ["c"], 3]];
// 1,不传参数时,默认“拉平”一层
console.log(arr.flat());
// [1, 'a', 'b', 2, ['c'], 3]
// 2,传入一个整数参数,整数即“拉平”的层数
console.log(arr.flat(2));
// [1, 'a', 'b', 2, 'c', 3]
// 3,Infinity 关键字作为参数时,无论多少层嵌套,都会转为一维数组
console.log(arr.flat(Infinity));
// [1, 'a', 'b', 2, 'c', 3]
// 4,传入 <=0 的整数将返回原数组,不“拉平”
console.log(arr.flat(0));
console.log(arr.flat(-6));
// [1, ['a', 'b'], [2, ['c'], 3]]
// 5,如果原数组有空位,flat()方法会跳过空位
console.log([1, 2, 3, 4, 5, 6, ,].flat());
// [1, 2, 3, 4, 5, 6]
flatMap:
flatMap()
方法对原数组的每个成员执行一个函数(相当于执行Array.prototype.map()
),然后对返回值组成的数组执行flat()
方法。该方法返回一个新数组,不改变原数组。
flatMap()
方法的参数是一个遍历函数,该函数可以接受三个参数,分别是当前数组成员、当前数组成员的位置(从零开始)、原数组。
let newFlatList = [[2, 4], [3, 6], [4, 8]].flatMap((item,index,arr) => {
console.log("item:",item);// [2, 4] [3, 6] [4, 8]
return [item[0]*2,item[1]*3] // 第一项*2,第二项*3,每个item都执行这个操作,最后flat扁平化数组并返回
});
console.log("newFlatList:",newFlatList);
//最终返回一维数组: [4, 12, 6, 18, 8, 24]
上面的案例是传入一个二维数组,把二维数组的第一项乘以2,第二项乘以3;最终返回处理好的一维数组;
注意:
flatMap()
只能展开一层数组reduce() 方法对数组中的每个元素按序执行一个由您提供的 reducer 函数,每一次运行 reducer
会将先前元素的计算结果作为参数传入,最后将其结果汇总为单个返回值。
请见这篇文章:ES6新增高阶函数reduce()讲解