概览
ES6引进了一些新的数组方法。
扩展运算符(Spread)+剩余操作符(Rest)
含义: 扩展运算符(spread)是三个点
...
,它如同rest参数的逆运算,将一个数组转为用逗号分隔的参数序列。
- 剩余操作符提取数组,用于剩余参数和结构。
- 扩展操作符将数组元素转换成函数调用的参数或者数组字面量的元素。
console.log(...[1, 2, 3]); // 1 2 3
console.log(1, ...[2, 3, 4], 5); //1 2 3 4 5
Rest(函数参数) 和 Spread(数组运算符) 主要是应用 ... 运算符,完成值的聚合和分解。
// 1.rest得到的是一个真正的数组,而不是一个伪数组。
const getOptions = function(...args){
console.log(args.join());
}
const arr = [123];
getOptions(...arr); // 123
// 2.rest可以搭配箭头函数使用,达到取得所有参数的目的。
const getOption = (...args)=>{
console.log(args);
}
let arrs = [1,2,3];
getOption(...arrs); // [1,2,3]
// 3.spread可以用于解构时,聚合所得的值。
const opt1 = 'one';
const opt2 = ['two','three','four'];
console.log([opt1,...opt2]); // ["one", "two", "three", "four"]
//4.spread可以用于数组定义。
const opts = ["one", "two", "three", "four"];
const config = ['other', ...opts]
该运算符主要用于函数的调用。
function push(array, ...items){
array.push(...items);
}
function add(x, y){
return x + y;
}
const numbers = [4, 38];
console.log(add(...numbers));; // 42
从上面的代码可以看出,该运算符可以将一个数组变成为参数序列。
function f(v, w, x, y, z){
console.log([v, w, x, y, z]);
}
const args = [0, 1];
f(-1, ...args, 2, ...[3]) // [-1, 0, 1, 2, 3]
扩展运算符后面放置表达式。
const arr = [
...(x > 0 ? ['a'] : []),
'b',
]
如果扩展运算符后面是一个空数组,则不产生任何效果。
console.log([...[], 1]); // [1]
扩展运算符应用
替代数组的apply方法:
// 由于扩展运算符可以展开数组,所以不需要使用apply方法将数组转为函数的参数。
// ES5 写法
function f(x,y,z){
console.log(x,y,z);
}
const args = [0,1,2];
f.apply(null,args) // 0 1 2
// ES6 写法
function f1(x,y,z){
console.log(x,y,z);
}
f1(...args) // 0 1 2
// ES5 写法
console.log(Math.max.apply(null,[14,3,77])); //77
// ES6 写法
console.log(Math.max(...[14,3,77])); //77
//等同于
console.log(Math.max(14,3,77)); //77
// ES5 写法
const arr1 = [0,1,2];
const arr2 = [3,4,5];
Array.prototype.push.apply(arr1,arr2);
console.log(arr1); // [0, 1, 2, 3, 4, 5]
// ES6 写法
const arr11 = [0,1,2];
const arr22 = [3,4,5];
arr11.push(...arr22);
console.log(arr11); // [0, 1, 2, 3, 4, 5]
// ES5 写法
console.log(new (Date.bind.apply(Date, [null , 2015, 1, 1]))); // Sun Feb 01 2015 00:00:00 GMT+0800 (中国标准时间)
// ES6 写法
console.log(new Date(...[2015, 1, 1])); // Sun Feb 01 2015 00:00:00 GMT+0800 (中国标准时间)
合并数组:
// ES5 写法
const more = [2,2,2];
console.log([1,2].concat(more)); // [1,2,2,2,2]
// ES6 写法
console.log([1,2,...more]); // [1,2,2,2,2]
// ES5 写法
const arr1 = ['a','b'];
const arr2 = ['c','d'];
const arr3 = ['e','f'];
const arr = arr1.concat(arr2,arr3)
console.log(arr); // ["a", "b", "c", "d", "e", "f"]
// ES6 写法
console.log([...arr1,...arr2,...arr3]); // ["a", "b", "c", "d", "e", "f"]
console.log([1, ...[2,3], 4]); // [1,2,3,4]
与解构赋值结合:
// ES5 写法
const list = [1,2];
const a = list[0];
const rest = list.slice(1);
console.log(a, rest); // 1 [2]
// ES6 写法
[a1, ...rest1] = list;
console.log(a1,rest1); // 1 [2]
// 其他例子
const [first,...res] = [1,2,3,4,5];
console.log(first,res); // 1 [2,3,4,5]
const [first1,...res1] = [];
console.log(first1,res1); //undefined []
const [first2,...res2] = ['foo'];
console.log(first2,res2); // 'foo' []
const [...butLast, last] = [1,2,3,4,5] // 报错 Rest element must be last element
const [first3,...middle, last] = [1,2,3,4,5] // 报错 Rest element must be last element
函数返回值:
JavaScript的函数只能返回一个值,如果需要返回多个值,只能返回数组或对象。扩展运算符提供了解决这个问题的一种变通方法。
字符串:
// 将字符串转为真正的数组
console.log([...'hello']); // ['h','e','l','l','o']
// 正确识别32位的Unicode
console.log([...'x\uD83D\uDE80y'].length); // 3
实现了Iterator接口的对象:
任何Iterator接口的对象,都可以用扩展运算符转为真正的数组。
对于那些没有部署Iterator接口的类似数组的对象,扩展运算符就无法将其转为真正的数组。
const arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
}
// ES5 转为数组
const arrNew1 = [].slice.call(arrayLike);
console.log(arrNew1); // ['a','b','c']
// ES6 转为数组
const arrNew2 = Array.from(arrayLike); // ['a', 'b', 'c']
console.log(arrNew2); // ['a', 'b', 'c']
// Uncaught TypeError: arrayLike is not iterable.
const arrNew3 = [...arrayLike];
Map和Set结构、Generator函数:
// Map结构
let map = new Map([
[1,'one'],
[2,'two'],
[3,'three'],
])
let arr1 = [...map.keys()];
let arr2 = [...map.values()];
console.log(arr1,arr2); // [1, 2, 3] ["one", "two", "three"]
// Set结构
let set = new Set([11,-1,6]);
let arrNew = [...set];
console.log(arrNew); // [11,-1,6]
// Generator函数
const go = function*(){
yield 1;
yield 2;
yield 3;
}
console.log([...go()]); // [1,2,3]
const obj = {
* [Symbol.iterator](){
yield 'a';
yield 'b';
yield 'c';
}
}
console.log([...obj]); // ['a','b','c']
// 对于没有Iterator接口的对象,使用扩展运算符会报错
const obj1 = {
a:1,
b:2,
}
console.log([...obj1]); // Uncaught TypeError: obj1 is not iterable
数组类方法Array.from()
Array.from()
方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterator)对象(包括ES6新增的数据结构Set和Map)。
类似数组对象转为数组:
let arrayLike = {
'0':'a',
'1':'b',
'2':'c',
length:3
}
// ES5 写法
let arrNew1 = [].slice.call(arrayLike);
console.log(arrNew1); // ['a','b','c']
// ES6 写法
let arrNew2 = Array.from(arrayLike);
console.log(arrNew2); // ['a','b','c']
NodeList对象转为数组:
Document
1
2
3
arguments对象转为数组:
function foo(x = 0,y = 12) {
let args = Array.from(arguments)
console.log(args);
}
foo(); // []
foo(1); // [1]
foo(1,2); // [1,2]
字符串(Iterator接口)转为数组:
console.log(Array.from('hello')); // ["h", "e", "l", "l", "o"]
Set结构(Iterator接口)转为数组:
let nameSet = new Set(['a','b']);
console.log(Array.from(nameSet)); // ["a", "b"]
操作数组:
console.log(Array.from([1,2,3])); // [1,2,3]
类似数组的对象,特殊情况:
console.log(Array.from({length:3})); // [undefined,undefined,undefined]
console.log(Array.from({length:2},()=>'jack')); // ["jack", "jack"]
转数组兼容性考虑:
const toArray = (()=>
Array.from?Array.from:obj=>[].slice.call(obj)
)();
将数组中布尔值为false的成员转为0:
console.log(Array.from([1,,2,,3],(n)=>n||0)); // [1, 0, 2, 0, 3]
返回各种数据的类型:
function typesOf(){
return Array.from(arguments,value=>typeof value)
}
console.log(typesOf(null,[],NaN)); // ["object", "object", "number"]
避免将大于\uFFFF的Unicode字符算作两个字符的bug:
function countSymbols(string){
return Array.from(string).length
}
数组类方法Array.of()
将一组值转换为数组,弥补数组构造函数Array的不足。
Array.of()
总是返回参数值组成的数组,如果没有参数,就返回一个空数组。
console.log(Array.of()); // []
console.log(Array.of(undefined)); //[undefined]
console.log(Array.of(null)); // [null]
console.log(Array.of(1)); // [1]
console.log(Array.of(1,2)); // [1,2]
console.log(Array.of(3,4,7)); // [3,4,7]
Array 子类的构造函数会继承所有的 Array 类的方法,包括 of() 方法
class MyArray extends Array {
}
console.log(MyArray.of(3, 11, 8) instanceof MyArray); // true
console.log(MyArray.of(3).length === 1); // true
数组原型方法copyWithin()
会在当前数组内部制定位置的成员复制到其他位置(会覆盖原有成员),会返回当前数组。
Array.prototype.copyWithin(target,start=0,end=this.length)
- target(必选):从当前位置开始替换数据。
- start(可选):从该位置开始读取数据,默认0.如果为负值,表示倒数。
- end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示倒数。
// 将3号位到最后一位复制0号位开始往后排
let arr1 = [1,2,3,4,5]
arr1.copyWithin(0,3);
console.log(arr1); // [4, 5, 3, 4, 5]
// 将3号位复制到0号位
let arr2 = [1,2,3,4,5]
arr2.copyWithin(0,3,4);
console.log(arr2); // [4, 2, 3, 4, 5]
// -2相当于3号位,-1相当于4号位
let arr3 = [1,2,3,4,5];
arr3.copyWithin(0,-2,-1);
console.log(arr3); // [4, 2,3 , 4, 5]
// 将3号位复制到0号位
let arr4 = [].copyWithin.call({length:5,3:1},0,3);
console.log(arr4); // {0: 1, 3: 1, length: 5}
// 将2号位到数组结束,复制到0号位
let arr5 = new Int32Array([1,2,3,4,5])
arr5.copyWithin(0,2)
console.log(arr5); // Int32Array(5) [3, 4, 5, 4, 5]
// 对于没有部署TypedArray的copyWithin方法的平台
// 需要采用下面的写法
let arr6 = [].copyWithin.call(new Int32Array([1,2,3,4,5]),0,3,4);
console.log(arr6); // Int32Array(5) [4, 2, 3, 4, 5]
数组原型方法find()
和findIndex()
find()
方法用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有的数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员,如果没有符合条件的成员,则返回undefined。
findIndex
方法与find()
方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1.
let num = [1,4,-5,10].find(n=>n < 0)
console.log(num); // -5
let num1 = [1,4,-5,10].find(n=>n > 11)
console.log(num1); // undefined
let num2 = [1,4,-5,10].findIndex(n=>n < 0)
console.log(num2); // -5
let num3 = [1,4,-5,10].findIndex(n=>n > 11)
console.log(num3); // -1
接受3个参数,依次为当前的值,当前的位置和原数组:
// find返回value
let num4 = [1,5,10,15].find(function(value,index,arr){
return value > 9;
})
console.log(num4); // 10
//findIndex返回index
let num5 = [1,5,10,15].findIndex(function(value,index,arr){
return value > 9;
})
console.log(num5); // 2
将数组的空缺作为undefined
处理:
let num6 = ['a',,'c'].find(x=>x===undefined)
console.log(num6); // undefined
let num7 = ['a',,'c'].findIndex(x=>x===undefined)
console.log(num7); // 1
通过findIndex
方法寻找NaN
: indexOf()
一个众所周知的局限在于它无法寻找NaN
.
console.log([NaN].indexOf(NaN)); // -1
console.log([NaN].findIndex(y=>Object.is(NaN,y))); // 0
// 也可以创建辅助函数`elemIs()`
function elemIs(x){
return Object.is.bind(Object, x)
}
console.log([NaN].findIndex(elemIs(NaN))); // 0
数组原型方法fill()
fill(value,start,end)
用给定的值填充数组:
console.log(['a','b','c'].fill(7)); // [7, 7, 7]
数组的空缺不会被特殊处理:
console.log(['a',,'c'].fill(7)); // [7, 7, 7]
console.log(new Array(3).fill(7)); // [7, 7, 7]
限定填充范围:
console.log(['a','b','c'].fill(7,1,2)); // ["a", 7, "c"]
数组原型方法keys()
、values()
和entries()
——用于遍历数组;
——都返回遍历器对象;
——可用for...of
遍历;
keys()
是对键名的遍历、values()
是对键值的遍历、entries()
是对键值对的遍历。
// `Array.from()` 把迭代器中的内容放入数组
console.log(Array.from(['a','b'].keys())); // [0, 1]
console.log(Array.from(['a','b'].values())); // ['a', 'b']
console.log(Array.from(['a','b'].entries())); // [[0, 'a'], [1, 'b']]
// 展开操作符(...)将迭代器转换成数组
console.log(...['a','b'].keys()); // 0 1
console.log(...['a','b'].values()); // 'a' 'b'
console.log(...['a','b'].entries()); // [0, 'a'] [1, 'b']
// `for..of`遍历
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"
数组原型方法includes()
includes()
方法判断某个数组是否包含给定的值,返回true
或者false
。
console.log([1,2,3].includes(2)); // true
console.log([1,2,3].includes(4)); // false
console.log([1,2,NaN].includes(NaN)); // true
// 第二个参数表示搜索的起始位置,默认为0。如果为负数,则表示倒数的位置,如果这时大于数组的长度则会重置为从0开始
console.log([1,2,3].includes(2,-4)); // true
console.log([1,2,3].includes(2,-3)); // true
console.log([1,2,3].includes(2,-2)); // true
console.log([1,2,3].includes(2,-1)); // false
console.log([1,2,3].includes(2,0)); // true
console.log([1,2,3].includes(2,1)); // true
console.log([1,2,3].includes(2,2)); // false
console.log([1,2,3].includes(2,3)); // false
console.log([1,2,3].includes(2,4)); // false