在javascript中,數組是最重要的數據結構,沒有之一,由於全部的數據結構均可以使用數組模擬和表達。能夠說掌握了數組,就掌握了js與數據操做的大部分核心功能。javascript
ES6給數組添加了一些新特性,而這些新特性到目前爲止徹底能夠運用到本身的業務層。前端
可是,開發者初次接觸ES6,不少東西都看得雲裏來霧裏,無從下手。在這一節中將總結有關於ES6給數組提供一些新特性的使用方法,讓用戶可以聽得懂,用的起來。java
一、新增數組建立方法面試
1.1 Array.from算法
Array.from的設計目的是快速便捷把一個相似數組的可迭代對象建立成一個新的數組實例。數組
通俗的講,只要一個對象有length,Array.from就能把它變成一個數組,返回新的數組,而不改變原對象。數據結構
let likeArr = { '0': 'a', '1': 'b', '2': 'c', length: 3 }; // ES5的寫法 var arr1 = [].slice.call(likeArr); // ['a', 'b', 'c'] // ES6的寫法 let arr2 = Array.from(likeArr); // ['a', 'b', 'c']
常見的相似數組的對象還有 DOM 操做返回的 NodeList 集合,以及函數內部的 arguments 對象。Array.from均可以將它們轉爲真正的數組。函數
// NodeList對象 let div = document.querySelectorAll('div'); console.log(div); // NodeList(8) [div#cst, div, div.gb_3, …] console.log(Array.from(div)); //(8) [div#cst, div, div.gb_3, …] // arguments對象 function foo() { var args = Array.from(arguments); console.log(args); } foo(1,2,34,666,333,663); // [1, 2, 34, 666, 333, 663]
Array.from 對 String,Set,Map 等擁有迭代器的對象也能夠進行轉換。this
// String Array.from('abc'); // ["a", "b", "c"] // Set Array.from(new Set(['abc', 'def'])); // ["abc", "def"] // Map Array.from(new Map([[1, 'abc'], [2, 'def']])); // [[1, 'abc'], [2, 'def']]
此外,生成一個從0到指定數字的新數組,Array.from能夠輕易的作到:spa
Array.from({length: 10}, (v, i) => i); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]複製代碼
Array.from 接收的第二個參數,做用相似於數組的map方法,用來對每一個元素進行處理,處理後的值放入返回的數組:
let arrayLike = [1,3,5]; Array.from(arrayLike, x => x+1); // [2, 4, 6] // 等同於 Array.from(arrayLike).map(x => x+1); // [2, 4, 6]
1.2 Array.of
當參數個數大於1時,Array() 纔會返回由參數組成的新數組。當參數個數只有一個時,其實是指定數組的長度。
Array() // [] Array(3) // [, , ,] Array(3, 4, 5) // [3, 4, 5]
因爲參數個數的不一樣,會致使 Array() 的行爲有差別。
然而 Array.of 彌補了數組構造函數 Array() 的不足,Array.of 將參數依次轉化爲數組中的一項,而後返回這個新數組,而無論這個參數是數字仍是其它,它的行爲很是統一。
Array.of() // [] Array.of(undefined) // [undefined] Array.of(3) // [3] Array.of(3, 4, 5) // [3, 4, 5]
Array.of 老是返回參數值組成的數組。若是沒有參數,就返回一個空數組。
二、新增數組修改方法
2.1 copyWithin
能夠在當前數組內部,將指定位置的數組項複製到其餘位置,會覆蓋原數組項,而後返回當前數組。使用該方法會修改當前數組。
Array.prototype.copyWithin(target, start = 0, end = this.length)
它接受三個參數:
(1)target(必需):從該位置開始替換數據。若是爲負值,表示倒數。
(2)start(可選):從該位置開始讀取數據,默認爲 0。若是爲負值,表示倒數。
(3)end(可選):到該位置前中止讀取數據,默認等於數組長度。若是爲負值,表示倒數。
這三個參數都應該是數值,若是不是,會自動轉爲數值。
['a','b','c','d','e','f','g'].copyWithin(0, 3) // ["d", "e", "f", "g", "e", "f", "g"]
上面代碼表示將從 3 號位 直到數組結束的成員('d','e','f','g'),複製到從 0 號位開始的位置,結果覆蓋了原來的 'a' 和 'b' 。
將3號位複製到0號位,能夠這麼寫:
['a','b','c','d','e','f','g'].copyWithin(0, 3, 4) // ["d", "b", "c", "d", "e", "f", "g"]
start、end 爲負數的狀況:
['a','b','c','d','e','f','g'].copyWithin(0, -3, -1) // ["e", "f", "c", "d", "e", "f", "g"]
start -3 指的是 'e',-3 - (-1)=2,就是用 'e' , 'f' 兩個元素替換掉從0開頭的 'a' , 'b'
2.2 fill
使用給定值,填充一個數組。
[1,2,3,4,5].fill('a'); // ["a", "a", "a", "a", "a"] new Array(3).fill(12) // [12, 12, 12]
fill 方法用於空數組的初始化很是方便。數組中已有的元素,會被所有抹去。
fill方法還能夠接受第二個和第三個參數,用於指定填充的起始位置和結束位置。
[1,2,3,4,5].fill('a',2,4); // [1, 2, "a", "a", 5]
fill 方法從 2 號位開始,向原數組填充 'a',直到 4 號位以前結束。
注意,若是填充的類型爲對象,那麼被賦值的是同一個內存地址的對象,而不是深拷貝對象。
let arr = new Array(3).fill({name: "aaa"}); arr[0].name = "bbb"; console.log(arr) // [{name: "bbb"}, {name: "bbb"}, {name: "bbb"}] let arr = new Array(3).fill([]); arr[0].push("ccc"); console.log(arr) // [["ccc"], ["ccc"], ["ccc"]]
三、新增數組查找遍歷方法
3.1 find 和 findIndex
find 返回數組中第一個知足條件的元素(若是有的話), 若是沒有,則返回undefined。
[1, 4, -5, 10].find((n) => n < 0) // -5 [1, 4, -5, 10].find((n) => n < -10) // undefined
find方法的回調函數能夠接受三個參數,依次爲當前的值、當前的位置和原數組。
[1, 5, 10, 15].find(function(value, index, arr) { return value > 9; }) // 10 [1, 5, 10, 15].find(function(value, index, arr) { return console.log(value, index, arr) }) 1 0 [1, 5, 10, 15] 5 1 [1, 5, 10, 15] 10 2 [1, 5, 10, 15] 15 3 [1, 5, 10, 15]
數組實例的 findIndex 方法的用法與 find 方法很相似,返回第一個符合條件的數組成員的位置,若是全部成員都不符合條件,則返回 -1。
[1, 5, 10, 15].findIndex(function(value, index, arr) { return value > 9; }) // 2
indexOf 方法沒法識別數組的 NaN 成員,可是 findIndex 方法能夠藉助 Object.is 方法作到。
['a','b',NaN,'c'].indexOf(NaN) // -1 ['a','b',NaN,'c'].findIndex(y => Object.is(NaN, y)) // 2
3.2 includes
方法返回一個布爾值,表示某個數組是否包含給定的值,與字符串的 includes 方法相似。
沒有該方法以前,咱們一般使用數組的 indexOf 方法,檢查是否包含某個值。
if (arr.indexOf(el) !== -1) { }
indexOf 方法有兩個缺點:
一是不夠語義化,它的含義是找到參數值的第一個出現位置,因此要去比較是否不等於 -1,表達起來不夠直觀。
二是,它內部使用嚴格相等運算符(===)進行判斷,這會致使對 NaN 的誤判。
[NaN].indexOf(NaN) // -1
includes 使用的是不同的判斷算法,就沒有這個問題。
[NaN].includes(NaN) // true
includes 第二個參數表示搜索的起始位置:
['a','b','c','d'].includes('c',2) //true ['a','b','c','d'].includes('c',3) //false
若是第二個參數爲負數,則表示從倒數第幾位向後搜索:
['a','b','c','d'].includes('c',-1) //false (-1指的是倒數第一位'd') ['a','b','c','d'].includes('c',-2) //true (-2指的是倒數第二位'c')
3.3 entries、keys、values
它們都返回一個遍歷器對象,均可以用 for...of 循環進行遍歷。
惟一的區別是 keys 是對鍵名的遍歷、 values 是對鍵值的遍歷, entries 是對鍵值對的遍歷。
for (let index of ['a', 'b', 'c'].keys()) { console.log(index); } // 0 // 1 // 2 for (let elem of ['a', 'b', 'c'].values()) { console.log(elem); } // 'a' // 'b' // 'c' for (let [index, elem] of ['a', 'b', 'c'].entries()) { console.log(index, elem); } // 0 "a" // 1 "b" // 2 "c"
四、數組降維方法
4.1 flat
說到數組拍平,咱們先看一道阿里面試題:
編寫一個 JavaScript 函數,接受一個僅包含數字的 多維數組 ,返回拍平之後的結果。例如傳入:[1, [[2], 3, 4], 5],返回 [1, 2, 3, 4, 5]。
(本題來源:阿里巴巴前端筆試題)
const arr = [1, [[2], 3, 4], 5]; const flatten = arr => { return arr.reduce((flat, toFlat) => { return flat.concat(Array.isArray(toFlat) ? flatten(toFlat) : toFlat); }, []); }; const res = flatten(arr); console.log(res);
OK,我知道你以爲它太複雜,我這裏只是展現一下數組拍平的應用場景,接下來,咱們詳細說說關於數組拍平的知識。
flat用於將嵌套的數組「拉平」。該方法返回一個新數組,對原數據沒有影響。
[1, 2, [3, 4]].flat() // [1, 2, 3, 4]
默認只會「拉平」一層,若是想要「拉平」多層的嵌套數組,能夠將flat方法的參數寫成一個整數,表示想要拉平的層數,默認爲1。
[1, 2, [3, [4, 5]]].flat(2) // [1, 2, 3, 4, 5]
若是數組嵌套的層數不固定怎麼辦?把flat裏的參數寫成1000或者100000?
這樣寫太死板了,能夠聯想到 Infinity (無窮大)啊。
無論有多少層嵌套,若是都要轉成一維數組,能夠用 Infinity 關鍵字做爲參數。
[1, [2, [3, [4]]]].flat(Infinity) // [1, 2, 3, 4]
4.2 flatMap
方法對原數組的每一個成員執行一個函數(至關於執行 Array.prototype.map),而後對返回值組成的數組執行 flat() 方法。該方法返回一個新數組,不改變原數組。
[2, 3, 4].flatMap((x) => [x, x * 2]) // [2, 4, 3, 6, 4, 8] // 至關於 [[2, 4], [3, 6], [4, 8]].flat()
注意:flatMap() 只能展開一層數組。
五、總結
數組新增的方法,一方面起到了加強型做用,一方面讓代碼變得更加簡潔。
其中 Array.from 和 Array.of 屬於構造函數方法。
從是否改變數組自身的角度看:
copyWithin、fill 會改變數組自身,
includes、flat、flatMap不會改變數組自身。
entries、keys、values、find、findeIndex屬於數組遍歷方法。