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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章