ECMAScript6 学习笔记(三):数组的扩展

扩展运算符

...ES6 中新增的 扩展运算符 。它可以将一个数组转换为用逗号分隔的参数序列。

console.log(...[1, 2, 3]);
// 同 console.log(1, 2, 3);

利用这个特性我们可以很方便的将实现了 Iterator 的对象转为数组:

[...document.getElementsByTagName('div')]
// [div, div, div, ...]

扩展运算符还可以用来取代函数的 apply 方法:

// ECMAScript5
function fn(x, y, z) {
	console.log(x + y + z); // 6
}
fn.apply(null, [1, 2, 3]);

// ECMAScript6
function fn(x, y, z) {
	console.log(x + y + z); // 6
}
fn(...[1, 2, 3]);

// ... 的实际运用
Math.max(...[1, 2, 3]); // 3

Math.max([1, 2, 3]); // NaN

Math.max.apply(null, [1, 2, 3]); // 3

其它实际运用:

1. 克隆简单数组

// 克隆简单数组
const array1 = [1, 2, 3];
// 写法一 (解构赋值)
const [...array2] = array1;
// 写法二 (扩展运算)
const array2 = [...array1];

通过 ... 运算符来拷贝数组的方法是浅拷贝,所以不能用来直接拷贝深数组。

2. 合并数组

const array1 = [1, 'a'];
const array2 = [2, 'b'];
const array3 = [3, { c: 'c' }]; // 应当避免此种拷贝
const array4 = [...array1, ...array2, ...array3];

arra4[5]['c'] === arra3[1]['c']; // true

3. 将字符串转为数组

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

使用此方法可以正确识别四个字节的 Unicode 字符,可用于字符统计。

'x\uD83D\uDE80y'.length // 4

// 注意前面的分号
;[...'x\uD83D\uDE80y'].length // 3



function length(str) {
	return [...str].length;
}
length('x\uD83D\uDE80y') // 3

4. 将任意实现了 Iterator 接口的对象转为数组

在写代码中我们经常遇到类数组不能使用 forEach 方法的问题,如:NodeList、HTMLCollection。之前我们做类型转换可能用到了:

// ECMAScript5
[].slice.call(HTMLCollection, null);

或者

// ECMAScript6
Array.from(HTMLCollection);

现在又多了一个方法:

[...document.getElementsByTagName('div')]

上面的 NodeListHTMLCollection 之所以可以使用扩展运算符将其转为真正的数组,原因就在于它们均实现了 Iterator

Number.prototype[Symbol.iterator] = function*() {
  let i = 0;
  let num = this.valueOf();
  while (i < num) {
    yield i++;
  }
}

console.log([...5]) // [0, 1, 2, 3, 4]

Array.from()

Array.from() 同样来自 ECMAScript6,它与扩展运算符的用途很相似,但又有很大的不同。

Array.from() 和 … 的区别

Array.from: 该方法支持所有的类数组对象,所谓类数组对象就是具有 length 属性的所有对象。因此,只要有 length 属性的对象都可以通过 Array.from 方法转为数组。

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

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

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

扩展运算符(…): 扩展运算符背后调用的是遍历器接口(Symbol.iterator),如果一个对象没有部署这个接口,就无法转换。

Array.of()

Array.of 方法用于将一组值,转为数组,是扩展运算符的逆运算。它的出现弥补了数组构造函数 Array() 的不足。

// 数组构造函数存在行为差异
Array() // []
Array(3) // [, , ,]
Array(3, 11, 8) // [3, 11, 8]

// Array.of 方法弥补了差异
Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1
Array.of(undefined) // [undefined]

ES5 中可以用下面的方法模拟实现 Array.of 方法。

function ArrayOf() {
	return [].slice.call(arguments);
}

find() 和 findIndex()

数组实例的 find 方法用于找出第一个符合条件的数组成员。它的参数是一个回调函数,该回调函数可以接收三个参数,依次代表: 当前值、当前索引、源数组。

[0, 1, -2, 3].find((item, index, array) => item < 0);
// -2

findIndex 方法与 find 方法相似。不同的是 findIndex 返回的是第一个符合条件数组成员的下标,find 方法返回的是第一个符合条件数组成员本身。

它们都可以接收第二个参数,用于指定回调函数的 this 指向:

[1, 2, 3, 4].find(item => {
	console.log(this); // Window
	return item === 1;
}, window);
// 1

fill()

fill 方法使用给定值填充数组。

[].fill(2); // []

[ , , ].fill(2); // [2, 2]

[1, 1, 1].fill(2); // [2, 2, 2]

new Array(3).fill(2); // [2, 2, 2]

fill 方法的第二参数和第三个参数代表要填充起始位置和结束位置:

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

entries()、keys()、values()

ES6 提供三个新的方法——entries(),keys()和values()——用于遍历数组。它们都返回一个遍历器对象,可以用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"

// 不使用 for..of..循环
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']

includes()

Array.prototype.includes 方法用于检测一个数组是否包含给定值,返回的是 Boolean 类型。

[1, 2, 3].includes(2)     // true
[1, 2, 3].includes(4)     // false
[1, 2, NaN].includes(NaN) // true
'hello'.includes('he') // true

该方法的第二个参数表示搜索的起始位置,默认为0。如果第二个参数为负数,则表示倒数的位置,如果这时它大于数组长度(比如第二个参数为-4,但数组长度为3),则会重置为从0开始。

[1, 2, 3].includes(3, 3);  // false
[1, 2, 3].includes(3, -1); // true

在没有 includes 方法前,我们通常使用 indexOf 方法来辅助判断数组中是否包含给定值。但是 indexOf 不够语义化,且使用 indexOf 方法不能判断 NaN 的成员。

[1, NaN, 3].indexOf(1) !== -1; // true
[1, NaN, 3].indexOf(NaN) !== -1; // false

flat() 和 flatMap

数组实例的 flat 方法可以将嵌套数组一层一层的展开,它接收一个参数,代表要展开多少层,值为 0 的时候代表不展开。

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

flatMap 方法相当于 map 方法和 flat 方法的组合

// 相当于 [[2, 4], [3, 6], [4, 8]].flat()
[2, 3, 4].flatMap((x) => [x, x * 2])
// [2, 4, 3, 6, 4, 8]

数组的空位

new Array(3); // [,,,]

虽然 [1, 1, 1, 1][ , , , ] 都有三个 ‘,’ 但是前者的长度是 4, 后者的长度是 3,请各位不要想当然的去猜测。

空位代表什么都没有,并不等同于 undefined

0 in [undefined]; // true
0 in [,]; // false

你可能感兴趣的:(ECMAScript)