## Array.from()
Array.from方法用于將兩類對象轉為真正的數組:類似數組的對象(array-like object)和可遍歷(iterable)的對象(包括ES6新增的數據結構Set和Map)。
~~~
let ps = document.querySelectorAll('p');
Array.from(ps).forEach(function (p) {
console.log(p);
});
~~~
上面代碼中,querySelectorAll方法返回的是一個類似數組的對象,只有將這個對象轉為真正的數組,才能使用forEach方法。
Array.from方法可以將函數的arguments對象,轉為數組。
~~~
function foo() {
var args = Array.from( arguments );
}
foo( "a", "b", "c" );
~~~
任何有length屬性的對象,都可以通過Array.from方法轉為數組。
~~~
Array.from({ 0: "a", 1: "b", 2: "c", length: 3 });
// [ "a", "b" , "c" ]
~~~
對于還沒有部署該方法的瀏覽器,可以用Array.prototyp.slice方法替代。
~~~
const toArray = (() =>
Array.from ? Array.from : obj => [].slice.call(obj)
)();
~~~
Array.from()還可以接受第二個參數,作用類似于數組的map方法,用來對每個元素進行處理。
~~~
Array.from(arrayLike, x => x * x);
// 等同于
Array.from(arrayLike).map(x => x * x);
~~~
下面的例子將數組中布爾值為false的成員轉為0。
~~~
Array.from([1, , 2, , 3], (n) => n || 0)
// [1, 0, 2, 0, 3]
~~~
Array.from()的一個應用是,將字符串轉為數組,然后返回字符串的長度。這樣可以避免JavaScript將大于\uFFFF的Unicode字符,算作兩個字符的bug。
~~~
function countSymbols(string) {
return Array.from(string).length;
}
~~~
## Array.of()
Array.of方法用于將一組值,轉換為數組。
~~~
Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1
~~~
這個方法的主要目的,是彌補數組構造函數Array()的不足。因為參數個數的不同,會導致Array()的行為有差異。
~~~
Array() // []
Array(3) // [undefined, undefined, undefined]
Array(3,11,8) // [3, 11, 8]
~~~
上面代碼說明,只有當參數個數不少于2個,Array()才會返回由參數組成的新數組。
Array.of方法可以用下面的代碼模擬實現。
~~~
function ArrayOf(){
return [].slice.call(arguments);
}
~~~
## 數組實例的find()和findIndex()
數組實例的find方法,用于找出第一個符合條件的數組成員。它的參數是一個回調函數,所有數組成員依次執行該回調函數,直到找出第一個返回值為true的成員,然后返回該成員。如果沒有符合條件的成員,則返回undefined。
~~~
var found = [1, 4, -5, 10].find((n) => n < 0);
console.log("found:", found);
~~~
上面代碼找出數組中第一個小于0的成員。
~~~
[1, 5, 10, 15].find(function(value, index, arr) {
return value > 9;
}) // 10
~~~
上面代碼中,find方法的回調函數可以接受三個參數,依次為當前的值、當前的位置和原數組。
數組實例的findIndex方法的用法與find方法非常類似,返回第一個符合條件的數組成員的位置,如果所有成員都不符合條件,則返回-1。
~~~
[1, 5, 10, 15].findIndex(function(value, index, arr) {
return value > 9;
}) // 2
~~~
這兩個方法都可以接受第二個參數,用來綁定回調函數的this對象。
另外,這兩個方法都可以發現NaN,彌補了數組的IndexOf方法的不足。
~~~
[NaN].indexOf(NaN)
// -1
[NaN].findIndex(y => Object.is(NaN, y))
// 0
~~~
上面代碼中,indexOf方法無法識別數組的NaN成員,但是findIndex方法可以借助Object.is方法做到。
## 數組實例的fill()
fill()使用給定值,填充一個數組。
~~~
['a', 'b', 'c'].fill(7)
// [7, 7, 7]
new Array(3).fill(7)
// [7, 7, 7]
~~~
上面代碼表明,fill方法用于空數組的初始化非常方便。數組中已有的元素,會被全部抹去。
fill()還可以接受第二個和第三個參數,用于指定填充的起始位置和結束位置。
~~~
['a', 'b', 'c'].fill(7, 1, 2)
// ['a', 7, '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"
~~~
## 數組實例的includes()
Array.protypeto.includes方法返回一個布爾值,表示某個數組是否包含給定的值。該方法屬于ES7。
~~~
[1, 2, 3].includes(2); // true
[1, 2, 3].includes(4); // false
[1, 2, NaN].includes(NaN); // true
~~~
該方法的第二個參數表示搜索的起始位置,默認為0。
~~~
[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true
~~~
下面代碼用來檢查當前環境是否支持該方法,如果不支持,部署一個簡易的替代版本。
~~~
const contains = (() =>
Array.prototype.includes
? (arr, value) => arr.includes(value)
: (arr, value) => arr.some(el => el === value)
)();
contains(["foo", "bar"], "baz"); // => false
~~~
## 數組推導
數組推導(array comprehension)提供簡潔寫法,允許直接通過現有數組生成新數組。這項功能本來是要放入ES6的,但是TC39委員會想繼續完善這項功能,讓其支持所有數據結構(內部調用iterator對象),不像現在只支持數組,所以就把它推遲到了ES7。Babel轉碼器已經支持這個功能。
~~~
var a1 = [1, 2, 3, 4];
var a2 = [for (i of a1) i * 2];
a2 // [2, 4, 6, 8]
~~~
上面代碼表示,通過for...of結構,數組a2直接在a1的基礎上生成。
注意,數組推導中,for...of結構總是寫在最前面,返回的表達式寫在最后面。
for...of后面還可以附加if語句,用來設定循環的限制條件。
~~~
var years = [ 1954, 1974, 1990, 2006, 2010, 2014 ];
[for (year of years) if (year > 2000) year];
// [ 2006, 2010, 2014 ]
[for (year of years) if (year > 2000) if(year < 2010) year];
// [ 2006]
[for (year of years) if (year > 2000 && year < 2010) year];
// [ 2006]
~~~
上面代碼表明,if語句寫在for...of與返回的表達式之間,可以使用多個if語句。
數組推導可以替代map和filter方法。
~~~
[for (i of [1, 2, 3]) i * i];
// 等價于
[1, 2, 3].map(function (i) { return i * i });
[for (i of [1,4,2,3,-8]) if (i < 3) i];
// 等價于
[1,4,2,3,-8].filter(function(i) { return i < 3 });
~~~
上面代碼說明,模擬map功能只要單純的for...of循環就行了,模擬filter功能除了for...of循環,還必須加上if語句。
在一個數組推導中,還可以使用多個for...of結構,構成多重循環。
~~~
var a1 = ["x1", "y1"];
var a2 = ["x2", "y2"];
var a3 = ["x3", "y3"];
[for (s of a1) for (w of a2) for (r of a3) console.log(s + w + r)];
// x1x2x3
// x1x2y3
// x1y2x3
// x1y2y3
// y1x2x3
// y1x2y3
// y1y2x3
// y1y2y3
~~~
上面代碼在一個數組推導之中,使用了三個for...of結構。
需要注意的是,數組推導的方括號構成了一個單獨的作用域,在這個方括號中聲明的變量類似于使用let語句聲明的變量。
由于字符串可以視為數組,因此字符串也可以直接用于數組推導。
~~~
[for (c of 'abcde') if (/[aeiou]/.test(c)) c].join('') // 'ae'
[for (c of 'abcde') c+'0'].join('') // 'a0b0c0d0e0'
~~~
上面代碼使用了數組推導,對字符串進行處理。
數組推導需要注意的地方是,新數組會立即在內存中生成。這時,如果原數組是一個很大的數組,將會非常耗費內存。
## Array.observe(),Array.unobserve()
這兩個方法用于監聽(取消監聽)數組的變化,指定回調函數。
它們的用法與Object.observe和Object.unobserve方法完全一致,也屬于ES7的一部分,請參閱《對象的擴展》一章。唯一的區別是,對象可監聽的變化一共有六種,而數組只有四種:add、update、delete、splice(數組的length屬性發生變化)。