鍍金池/ 教程/ HTML/ 數(shù)組的擴(kuò)展
數(shù)組的擴(kuò)展
Class和Module
Set 和 Map 數(shù)據(jù)結(jié)構(gòu)
異步操作
對象的擴(kuò)展
Generator 函數(shù)
數(shù)值的擴(kuò)展
變量的解構(gòu)賦值
Iterator 和 for...of 循環(huán)
Promise 對象
參考鏈接
ECMAScript 6簡介
作者簡介
字符串的擴(kuò)展
編程風(fēng)格
let 和 const 命令
函數(shù)的擴(kuò)展

數(shù)組的擴(kuò)展

Array.from()

Array.from 方法用于將兩類對象轉(zhuǎn)為真正的數(shù)組:類似數(shù)組的對象(array-like object)和可遍歷(iterable)的對象(包括ES6新增的數(shù)據(jù)結(jié)構(gòu) Set 和 Map)。


let ps = document.querySelectorAll('p');

Array.from(ps).forEach(function (p) {
  console.log(p);
});

上面代碼中,querySelectorAll 方法返回的是一個類似數(shù)組的對象,只有將這個對象轉(zhuǎn)為真正的數(shù)組,才能使用 forEach 方法。

Array.from 方法可以將函數(shù)的 arguments 對象,轉(zhuǎn)為數(shù)組。


function foo() {
  var args = Array.from( arguments );
}

foo( "a", "b", "c" );

任何有 length 屬性的對象,都可以通過 Array.from 方法轉(zhuǎn)為數(shù)組。


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()還可以接受第二個參數(shù),作用類似于數(shù)組的 map 方法,用來對每個元素進(jìn)行處理。


Array.from(arrayLike, x => x * x);
// 等同于
Array.from(arrayLike).map(x => x * x);

下面的例子將數(shù)組中布爾值為 false 的成員轉(zhuǎn)為 0。

Array.from([1, , 2, , 3], (n) => n || 0)
// [1, 0, 2, 0, 3]

Array.from()的一個應(yīng)用是,將字符串轉(zhuǎn)為數(shù)組,然后返回字符串的長度。這樣可以避免 JavaScript 將大于\uFFFF的 Unicode 字符,算作兩個字符的bug。

function countSymbols(string) {
  return Array.from(string).length;
}

Array.of()

Array.of 方法用于將一組值,轉(zhuǎn)換為數(shù)組。


Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1

這個方法的主要目的,是彌補(bǔ)數(shù)組構(gòu)造函數(shù) Array() 的不足。因為參數(shù)個數(shù)的不同,會導(dǎo)致 Array() 的行為有差異。

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

上面代碼說明,只有當(dāng)參數(shù)個數(shù)不少于 2 個,Array() 才會返回由參數(shù)組成的新數(shù)組。

Array.of 方法可以用下面的代碼模擬實現(xiàn)。

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

數(shù)組實例的 find() 和 findIndex()

數(shù)組實例的 find 方法,用于找出第一個符合條件的數(shù)組成員。它的參數(shù)是一個回調(diào)函數(shù),所有數(shù)組成員依次執(zhí)行該回調(diào)函數(shù),直到找出第一個返回值為 true 的成員,然后返回該成員。如果沒有符合條件的成員,則返回 undefined。

var found = [1, 4, -5, 10].find((n) => n < 0);
console.log("found:", found);

上面代碼找出數(shù)組中第一個小于 0 的成員。

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

上面代碼中,find 方法的回調(diào)函數(shù)可以接受三個參數(shù),依次為當(dāng)前的值、當(dāng)前的位置和原數(shù)組。

數(shù)組實例的 findIndex 方法的用法與 find 方法非常類似,返回第一個符合條件的數(shù)組成員的位置,如果所有成員都不符合條件,則返回 -1。


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

這兩個方法都可以接受第二個參數(shù),用來綁定回調(diào)函數(shù)的 this 對象。

另外,這兩個方法都可以發(fā)現(xiàn) NaN,彌補(bǔ)了數(shù)組的 IndexOf 方法的不足。

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

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

上面代碼中,indexOf 方法無法識別數(shù)組的 NaN 成員,但是 findIndex 方法可以借助 Object.is 方法做到。

數(shù)組實例的 fill()

fill() 使用給定值,填充一個數(shù)組。


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

new Array(3).fill(7)
// [7, 7, 7]

上面代碼表明,fill 方法用于空數(shù)組的初始化非常方便。數(shù)組中已有的元素,會被全部抹去。

fill() 還可以接受第二個和第三個參數(shù),用于指定填充的起始位置和結(jié)束位置。


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

數(shù)組實例的 entries(),keys() 和 values()

ES6 提供三個新的方法——entries(),keys() 和 values()——用于遍歷數(shù)組。它們都返回一個遍歷器,可以用 for...of 循環(huán)進(jìn)行遍歷,唯一的區(qū)別是 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"

數(shù)組實例的 includes()

Array.protypeto.includes 方法返回一個布爾值,表示某個數(shù)組是否包含給定的值。該方法屬于 ES7。


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

該方法的第二個參數(shù)表示搜索的起始位置,默認(rèn)為 0。


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

下面代碼用來檢查當(dāng)前環(huán)境是否支持該方法,如果不支持,部署一個簡易的替代版本。


const contains = (() =>
  Array.prototype.includes
    ? (arr, value) => arr.includes(value)
    : (arr, value) => arr.some(el => el === value)
)();
contains(["foo", "bar"], "baz"); // => false

數(shù)組推導(dǎo)

ES6 提供簡潔寫法,允許直接通過現(xiàn)有數(shù)組生成新數(shù)組,這被稱為數(shù)組推導(dǎo)(array comprehension)。


var a1 = [1, 2, 3, 4];
var a2 = [for (i of a1) i * 2];

a2 // [2, 4, 6, 8]

上面代碼表示,通過 for...of 結(jié)構(gòu),數(shù)組 a2 直接在 a1 的基礎(chǔ)上生成。

注意,數(shù)組推導(dǎo)中,for...of 結(jié)構(gòu)總是寫在最前面,返回的表達(dá)式寫在最后面。

for...of 后面還可以附加 if 語句,用來設(shè)定循環(huán)的限制條件。


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 與返回的表達(dá)式之間,可以使用多個 if 語句。

數(shù)組推導(dǎo)可以替代 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 循環(huán)就行了,模擬 filter 功能除了 for...of 循環(huán),還必須加上 if 語句。

在一個數(shù)組推導(dǎo)中,還可以使用多個 for...of 結(jié)構(gòu),構(gòu)成多重循環(huán)。


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

上面代碼在一個數(shù)組推導(dǎo)之中,使用了三個 for...of 結(jié)構(gòu)。

需要注意的是,數(shù)組推導(dǎo)的方括號構(gòu)成了一個單獨(dú)的作用域,在這個方括號中聲明的變量類似于使用 let 語句聲明的變量。

由于字符串可以視為數(shù)組,因此字符串也可以直接用于數(shù)組推導(dǎo)。


[for (c of 'abcde') if (/[aeiou]/.test(c)) c].join('') // 'ae'

[for (c of 'abcde') c+'0'].join('') // 'a0b0c0d0e0'

上面代碼使用了數(shù)組推導(dǎo),對字符串進(jìn)行處理。

數(shù)組推導(dǎo)需要注意的地方是,新數(shù)組會立即在內(nèi)存中生成。這時,如果原數(shù)組是一個很大的數(shù)組,將會非常耗費(fèi)內(nèi)存。

Array.observe(),Array.unobserve()

這兩個方法用于監(jiān)聽(取消監(jiān)聽)數(shù)組的變化,指定回調(diào)函數(shù)。

它們的用法與 Object.observe 和 Object.unobserve 方法完全一致,也屬于 ES7 的一部分,請參閱《對象的擴(kuò)展》一章。唯一的區(qū)別是,對象可監(jiān)聽的變化一共有六種,而數(shù)組只有四種:add、update、delete、splice(數(shù)組的 length 屬性發(fā)生變化)。