擴展運算符
...
爲 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')]
上面的 NodeList
或 HTMLCollection
之所以可以使用擴展運算符將其轉爲真正的數組,原因就在於它們均實現了 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