ES6 数组相关

207 阅读12分钟

1.1扩展运算符(...)

1.1.1 用于函数调用

将一个数组变为参数序列;可与正常的函数参数结合使用;扩展运算符后面也可以放表达式;如果扩展运算符后面是空数组,不产生任何效果。只有函数调用时,扩展运算符才可以放到圆括号里。

const array1 = [];
const array2 = [];
const arr = [1,2,3];
function push(array,...items){
  array.push(...items);
  console.log(...(items+'1')); // 1 , 2 , 3 1
}
push(array1,...arr);
push(array2,...[]);

array1 // [1,2,3]
array2 //[]

1.1.2 替代Apply方法

apply方法的第二个参数把数组转换为函数的参数,有了扩展运算符,可以替代apply的这个作用

function f(x, y, z) {
  // ...
}

// ES5 的写法
var args = [0, 1, 2];
f.apply(null, args);

// ES6的写法
let args = [0, 1, 2];
f(...args);

1.1.3 复制数组

数组是复合数据类型,直接复制只是复制了数组的地址,没有重新创建一个数组。扩展运算符可以提供复制数组的简便方法。如果数组成员是对象,只能复制对象的引用,如果修改了新数组成员,会同步反映到原数组(浅拷贝)

const a1 = [1, 2];
const a2 = a1;
// 只复制了a1的地址
a2[0] = 2;
a1 // [2, 2]

// ES5
const a2 = a1.concat();
a2[0] = 2;
a1 // [1, 2]

// 扩展运算符方法
const a2 = [...a1]; // 写法一
const [...a2] = a1; // 写法二

1.1.4 合并数组

提供了数组合并的新写法

const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];

// ES5 的合并数组
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]

// ES6 的合并数组
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]

1.1.5 结合解构赋值

扩展运算符可以与解构赋值结合生成数组,只能把扩展运算符,会报错

const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest  // [2, 3, 4, 5]

const [first, ...middle, last] = [1, 2, 3, 4, 5];
// 报错

1.1.6 将字符串转为数组

[...'hello']
// [ "h", "e", "l", "l", "o" ]

1.1.7 将==定义了Iterator==接口的对象转换为数组

let nodeList = document.querySelectorAll('div');
let array = [...nodeList];

let arrayLike = {
  '0': 'a',
  '1': 'b',
  '2': 'c',
  length: 3
};
// 没有部署Iterator接口的类似数组的对象
// TypeError: Cannot spread non-iterable object.
let arr = [...arrayLike];

1.1.8 带有Iterator接口的对象可使用扩展运算符

扩展运算符背后调用的是遍历器接口

// map结构
let map = new Map([
  [1, 'one'],
  [2, 'two'],
  [3, 'three'],
]);
let arr = [...map.keys()]; // [1, 2, 3]

// generator
const go = function*(){
  yield 1;
  yield 2;
  yield 3;
};
[...go()] // [1, 2, 3]

1.2Array.from()

用于将类似数组的对象和可遍历的对象转为真正的数组。==对象里必须有length属性==,任何有length属性对对象都可以通过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']

Array.from({ length: 3 });
// [ undefined, undefined, undefined ]

Array.from()还可以接受第二个参数,作用类似数组的map方法,用来对每个元素进行处理,处理后放入数组。

Array.from([1, 2, 3], (x) => x * x)
// [1, 4, 9]

1.3Array.of()

将一组值转为数组,这个函数主要为了弥补构造函数Array()因为参数个数不同,导致Array()行为有差异的不足。

Array() // []
Array(3) // [, , ,]
Array(3, 11, 8) // [3, 11, 8]

Array.of() // []
Array.of(undefined) // [undefined]
Array.of(3) // [3]
Array.of(1, 2) // [1, 2]

1.4数组实例的copyWithin()

把当前数组内部指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。可以修改当前数组。

参数:

  1. target(必须):从该位置开始替换数据。如果为负值,表示倒数。
  2. start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示从末尾开始计算。
  3. end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示从末尾开始计算。
// 将3号位复制到0号位
[1, 2, 3, 4, 5].copyWithin(0, 3, 4)
// [4, 2, 3, 4, 5]

// -2相当于3号位,-1相当于4号位
[1, 2, 3, 4, 5].copyWithin(0, -2, -1)
// [4, 2, 3, 4, 5]

// 将3号位复制到0号位
[].copyWithin.call({length: 5, 3: 1}, 0, 3)
// {0: 1, 3: 1, length: 5}

// 将2号位到数组结束,复制到0号位
let i32a = new Int32Array([1, 2, 3, 4, 5]);
i32a.copyWithin(0, 2);
// Int32Array [3, 4, 5, 4, 5]

1.5数组实例find(),findIndex()

1.find方法:用于找出第一个符合条件的数组成员。参数是一个回调函数,所有数组成员依次执行该回调函数,知道找到第一个返回值为true的成员,然后返回该成员。find方法的回调参数可以接受三个参数,依次为当前值、当前位置和原数组。

[1, 5, 10, 15].find(function(value, index, arr) {
  return value > 9;
}) // 10

2.findIndex方法:和find类似,返回符合条件的成员位置。

这两个方法可以接受第二个参数,用来绑定回调函数的this对象。

function f(v){
  return v > this.age;
}
let person = {name: 'John', age: 20};
[10, 12, 26, 15].find(f, person);    // 26

可以发现NaN,弥补了indexOf方法的不足。

[NaN].indexOf(NaN)
// -1

[NaN].findIndex(y => Object.is(NaN, y))
// 0

1.6数组实例fill()

使用给定值,填充数组

参数:

  1. value:用来填充数组元素的值
  2. start(可选):起始索引,默认为0
  3. end(可选):终止索引,默认为this.length

如果参数只有第一个,会把数组所有元素覆盖,适合用于给空数组赋初值。

['a', 'b', 'c'].fill(7, 1, 2)
// ['a', 7, 'c']

['a', 'b', 'c'].fill(7)
// [7, 7, 7]

1.7数组实例entries(),keys(),values()

  1. 三个方法的相同点:都是用于遍历数组。返回一个遍历器对象。
  2. 区别:entries遍历键值对,keys遍历键名,values遍历键值。
console.log(...['a','b','c'].keys()); // 0 1 2
console.log(...['a','b','c'].values()); // a b c
console.log(...['a','b','c'].entries()); // [ 0, 'a' ] [ 1, 'b' ] [ 2, 'c' ]
  1. 遍历 可以用for...of循环遍历,也可以手动调用遍历器的next方法。
// for...of
for (let elem of ['a', 'b','c'].entries()) {
  console.log(elem);
}

// 遍历器
let letter = ['a', 'b', 'c'];
let entries = letter.entries();
console.log(entries.next().value); // [0, 'a']
console.log(entries.next().value); // [1, 'b']
console.log(entries.next().value); // [2, 'c']

1.8数组实例includes()

表示数组是否包含给定的值。

参数:

  1. valueToFind:需要查找的元素值
  2. fromIndex(可选):从fromIndex索引处开始查找。如果第二个参数为负数,则表示倒数的位置,如果这时它大于数组长度(比如第二个参数为-4,但数组长度为3),则会重置为从0开始。
[1, 2, 3].includes(3, 3);  // false
[1, 2, 3].includes(3, -1); // true
[1, 2, 3].includes(3,-4); //true
[1, 2, NaN].includes(NaN); // true

与Map,Set的has方法的区别:

  1. Map的has方法:查找键名
  2. Set的has方法:查找值

1.9数组实例的flat(),flatMap()

1.flat():用于将嵌套的数组拉平,返回一个一维数组,对原数据没有影响。参数:depth-指定要提取嵌套数组的结构深度,默认值为1。如果不管有多少层嵌套,都要转成一维数组,可以用Infinity关键字作为参数。如果原数组有空位,flat()方法会跳过空位。

[1, 2, [3, [4, 5]]].flat()
// [1, 2, 3, [4, 5]]

[1, 2, [3, [4, 5]]].flat(2)
// [1, 2, 3, 4, 5]

[1, [2, [3]]].flat(Infinity)
// [1, 2, 3]

2.flatMap():首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。

参数:

  1. callback-可以生成一个新数组中的元素的函数,可以传入三个参数:currentValue(当前正在数组中处理的元素),index(可选,数组中正在处理的当前元素的索引), array(可选,被调用的 map 数组)
  2. thisArg-可选,执行 callback 函数时 使用的this值
// 相当于 [[2, 4], [3, 6], [4, 8]].flat()
[2, 3, 4].flatMap((x) => [x, x * 2])
// [2, 4, 3, 6, 4, 8]

// flatMap只能展开一层数组
// 相当于 [[[2]], [[4]], [[6]], [[8]]].flat()
[1, 2, 3, 4].flatMap(x => [[x * 2]])
// [[2], [4], [6], [8]]

1.10数组的空位

数组对空位是指数组对某一位置没有任何值,空位不是undefined,一个位置值等于undefined仍然是有值。

ES5对空位的处理不一致,大多数情况忽略空位。ES6明确将空位转为undefined。上述ES6新增的数组方法都不会忽略空位。

// entries()
[...[,'a'].entries()] // [[0,undefined], [1,"a"]]

// keys()
[...[,'a'].keys()] // [0,1]

// values()
[...[,'a'].values()] // [undefined,"a"]

// find()
[,'a'].find(x => true) // undefined

// findIndex()
[,'a'].findIndex(x => true) // 0

1.11数组常用方法(ES5的)

1.11.1 some和every方法

  1. some():测试是否==至少有一个==元素可以通过被提供的函数方法。返回Boolean类型
  2. every():测试是否==所有元素==都能通过被提供的函数方法。返回Boolean类型。

参数(两个方法参数一致):

  1. callback-测试元素的函数,可接受三个参数:element(测试的当前值);index(可选,测试的当前值的索引);array(可选,调用some,every的当前数组)
  2. thisArg-执行callback使用的this值
const hasProvider = providerList.some(item => item.providerNumber === providerNumber);

arr2.every((item, index) => item === arr1[index])

1.11.2 filter方法

创建一个新数组,其包含通过所提供函数实现的测试的所有元素。

参数(两个方法参数一致):

  1. callback-测试元素的函数,可接受三个参数:element(测试的当前值);index(可选,测试的当前值的索引);array(可选,调用的当前数组)
  2. thisArg-执行callback使用的this值
const newlist = list.filter(item => item.channelNumber === channelNumber);

1.11.3 map方法

创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。

参数:

  1. callback-测试元素的函数,可接受三个参数:currentValue(测试的当前值);index(可选,测试的当前值的索引);array(可选,调用的当前数组)
  2. thisArg-执行callback使用的this值
const channelNumberList = channelList.map(item => item.channelNumber)

1.11.4 forEach方法

对数组的每个元素执行一次提供的函数

参数:

  1. callback-测试元素的函数,可接受三个参数:currentValue(测试的当前值);index(可选,测试的当前值的索引);array(可选,调用的当前数组)
  2. thisArg-执行callback使用的this值
var array1 = ['a', 'b', 'c'];

array1.forEach(function(element) {
  console.log(element);
});

1.11.5 push(), pop(), shift(), unshift()方法

1.push():将一个或多个元素添加到数组末尾,并返回数组新长度。

参数:elementN-被添加到数组末尾到n个元素(按顺序添加)

可以用来合并两个数组

var vegetables = ['parsnip', 'potato'];
var moreVegs = ['celery', 'beetroot'];

// 将第二个数组融合进第一个数组
// 相当于 vegetables.push('celery', 'beetroot');
Array.prototype.push.apply(vegetables, moreVegs);

console.log(vegetables); 
// ['parsnip', 'potato', 'celery', 'beetroot']

2.pop():删除数组最后一个元素,返回该元素的值。

对于空数组使用pop方法,返回undefined。

var arr = ['a', 'b', 'c'];

arr.pop() // 'c'
arr // ['a', 'b']
[].pop() // undefined

3.shift():删除数组第一个元素,返回该元素的值。

var a = ['a', 'b', 'c'];

a.shift() // 'a'
a // ['b', 'c']

push和shift方法结合使用,构成了先进先出的队列结构。

4.unshift():在数组第一个位置添加元素,返回添加新元素后数组长度。可接受多个参数。

var a = ['a', 'b', 'c'];

a.unshift('x'); // 4
a // ['x', 'a', 'b', 'c']

1.11.6 join(), concat()方法

1.join():指定参数作为分隔符,将所有数组成员连接成字符串返回。不提供参数默认用逗号分隔。

var a = [1, 2, 3, 4];

a.join(' ') // '1 2 3 4'
a.join(' | ') // "1 | 2 | 3 | 4"
a.join() // "1,2,3,4"

2.concat():用于多个数组的合并,将新数组的成员加到原数组成员后部,==返回一个新数组==,原数组不变。

参数:valueN-将数组和/或值连接成新数组

['hello'].concat(['world'])
// ["hello", "world"]

['hello'].concat(['world'], ['!'])
// ["hello", "world", "!"]

[].concat({a: 1}, {b: 2})
// [{ a: 1 }, { b: 2 }]

[2].concat({a: 1})
// [2, {a: 1}]

1.11.7 slice(), splice()方法

1.slice():提取目标数组的一部分,返回一个新数组,原数组不变。

参数:

1)begin 可选 提取起始处的索引,从该索引开始提取原数组元素,默认为 0。

如果该参数为负数,则表示从原数组中的倒数第几个元素开始提取,slice(-2)表示提取原数组中的倒数第二个元素到最后一个元素(包含最后一个元素)。

如果省略 begin,则 slice 从索引 0 开始。

如果 begin 大于原数组的长度,则会返回空数组。

2)end 可选 提取终止处的索引,在该索引处结束提取原数组元素,默认为 0。slice 会提取原数组中索引从 begin 到 end。

如果该参数为负数, 则它表示在原数组中的倒数第几个元素结束抽取。 slice(-2,-1) 表示抽取了原数组中的倒数第二个元素到最后一个元素(不包含最后一个元素,也就是只有倒数第二个元素)。

如果 end 被省略,则slice 会一直提取到原数组末尾。

如果 end 大于数组的长度,slice也会一直提取到原数组末尾。

var a = ['a', 'b', 'c'];

a.slice(0) // ["a", "b", "c"]
a.slice(1) // ["b", "c"]
a.slice(1, 2) // ["b"]
a.slice(2, 6) // ["c"]
a.slice() // ["a", "b", "c"]

2.splice():用于删除原数组的一部分成员,并可以在删除的位置添加新的数组成员,返回值是被删除的元素。注意,==该方法会改变原数组==。

参数:

1)start-指定修改的开始位置(从0计数)

如果超出了数组的长度,则从数组末尾开始添加内容;

如果是负值,则表示从数组末位开始的第几位(从-1计数,这意味着-n是倒数第n个元素并且等价于array.length-n);

如果负数的绝对值大于数组的长度,则表示开始位置为第0位。

2)deleteCount-可选,整数,表示要移除的数组元素的个数。

如果 deleteCount 大于 start 之后的元素的总数,则从 start 后面的元素都将被删除(含第 start 位)。

如果 deleteCount被省略了,或者它的值大于等于array.length-start(也就是说,如果它大于或者等于start之后的所有元素的数量),那么start之后数组的所有元素都会被删除。

如果 deleteCount 是 0 或者负数,则不移除元素。这种情况下,至少应添加一个新元素。

3)item1, item2, ... 可选

要添加进数组的元素,从start 位置开始。如果不指定,则 splice() 将只删除数组元素。

var a = ['a', 'b', 'c', 'd', 'e', 'f'];
a.splice(4, 2, 1, 2) // ["e", "f"]
a // ["a", "b", "c", "d", 1, 2]

摘自阮一峰

参考MDN