js陣列方法集合

黃不逗發表於2020-12-03

js陣列方法集合

原文地址: https://juejin.cn/post/6901621560444977160#heading-1

(手機端可能看不清)獲取高清PDF,請在微信公眾號【小獅子前端Vue】回覆【陣列方法】

閱讀須知

開門見山,我先介紹一下本文整體目錄結構,介紹我將輸出的大概內容。

顯然,陣列的方法有很多很多,但是實際工作或者面試過程中有些用到的少之又少,因此,我不會將所有的方法都提出來,然後解析。那麼,我會將重要程度比較高的方法儘量用詳細簡潔的語言帶過,同時例舉幾個樣例,自測案例為你加深鞏固。

其次,本文還會提及面試常考問題,比如經典扁平化問題,陣列去重合併、排序、改變原陣列的方法不改變原陣列的方法等等。

好了,讓我們進入正文~

陣列的遍歷

猶記得今年快手面試官就問了一道經典面試題:說說你所知道的陣列的遍歷方法?

我想這個問題小夥伴們面試時應該也會遇到過吧,面試官問這個問題其一就是考察你對陣列方法掌握程度,其二可以通過你說的方法選擇其中一個繼續深度詢問,比如 reducemap使用等等,因此,我將 陣列遍歷 作為第一個模組來講解。

for 迴圈

let arr = [1, 2, 3, 4, 5];
for (let i = 0; i < arr.length; i++) {
  console.log(arr[i]);
}
// 1
// 2
// 3
// 4
// 5
複製程式碼

for..of 迴圈

摘自MDN上一句話,for...of 語句遍歷可迭代物件定義要迭代的資料。簡單來說,for...of遍歷的就是 value

let arr = ['Chocolate', 'zhlll', 'lionkk'];

for (let val of arr) {
console.log(val);
}
// Chocolate
// zhlll
// lionkk
複製程式碼

for...in 迴圈

摘自MDN上一句話,for...in 語句以任意順序迭代物件的可列舉屬性。簡單來說,for...in遍歷的就是 key。對於陣列,key對應著的是陣列的下標索引

let arr = ['Chocolate', 'zhlll', 'lionkk'];

for (let key in arr) {
console.log(key);
}
// 0
// 1
// 2
複製程式碼

array.forEach() 方法

先來介紹語法:

array.forEach(callback(currentValue, index, arr), thisArg)
複製程式碼

callback:為陣列中每個元素執行的函式,該函式接收一至三個引數

currentValue 陣列中正在處理的當前元素 index (可選) 陣列中正在處理的當前元素的索引 arr (可選) forEach() 方法正在操作的陣列 thisArg 可選引數,當執行回撥函式callback,用作this值

簡單例子:

let arr = ['Chocolate', 'zhlll', 'lionkk'];

arr.forEach(function (cur, index, arr) {
console.log(cur, index, arr);
})

// Chocolate 0 [ ‘Chocolate’, ‘zhlll’, ‘lionkk’ ]
// zhlll 1 [ ‘Chocolate’, ‘zhlll’, ‘lionkk’ ]
// lionkk 2 [ ‘Chocolate’, ‘zhlll’, ‘lionkk’ ]
複製程式碼

從上述例子中,瞭解到 forEach需要傳遞一個回撥函式,而那三個引數,後面兩個是可選的,那麼如何讓程式碼更加優雅美觀一點呢,同時,後面兩個引數按需新增即可:

let arr = ['Chocolate', 'zhlll', 'lionkk'];

arr.forEach((cur) => {
console.log(cur);
})
// Chocolate
// zhlll
// lionkk
複製程式碼

疑難點,我想小夥伴們,應該對最後一個 thisArg 有疑問吧,現在就來解釋一下:

function Foo() {
  this.sum = 0;
  this.cnt = 0;
}
// 在原型上新增一個名為 doSth 方法
Foo.prototype.doSth = function (arr) {
  arr.forEach(function (cur) {
    this.sum += cur;
    this.cnt++;
  }, this) // this 指向例項物件
}

let foo = new Foo();
let arr = [1, 2, 3];
foo.doSth(arr);

console.log(foo.sum, foo.cnt);
// 6 3
// 解釋: 6 === (1+2+3) 3 === (1+1+1)
複製程式碼

注意:如果使用箭頭函式表示式來傳入函式引數, thisArg 引數會被忽略,因為箭頭函式在詞法上繫結了 this 值。

因此,如果對於普通函式的話,可以看做是將 this 通過傳參的形式解決無法繼承 問題,當然,通過箭頭函式的方式是一個不錯的選擇!

map 遍歷

定義:返回一個新陣列,其結果是該陣列中的每個元素是呼叫一次提供的回撥函式後的返回值。

先來介紹語法:

let newArray = array.map(function(currentValue, index, arr), thisArg)
複製程式碼

callback:為陣列中每個元素執行的函式,該函式接收一至三個引數

currentValue 陣列中正在處理的當前元素 index (可選) 陣列中正在處理的當前元素的索引 arr (可選) map() 方法正在操作的陣列 thisArg 可選引數,當執行回撥函式callback,用作this值

簡單例子:

let arr = ['Chocolate', 'zhlll', 'lionkk'];

let newArr = arr.map(function (cur, index, arr) {
console.log(cur, index, arr);
return cur + index;
})
// Chocolate 0 [ ‘Chocolate’, ‘zhlll’, ‘lionkk’ ]
// zhlll 1 [ ‘Chocolate’, ‘zhlll’, ‘lionkk’ ]
// lionkk 2 [ ‘Chocolate’, ‘zhlll’, ‘lionkk’ ]

console.log(newArr)
// [ ‘Chocolate0’, ‘zhlll1’, ‘lionkk2’ ]
複製程式碼

疑難點,我想小夥伴們,有了前置問題了,這次理解 thisArg 應該沒有太多問題了吧,看看下面例子:

function Foo() {
  this.sum = 0;
  this.cnt = 0;
}
// 在原型上新增一個名為 doSth 方法
Foo.prototype.doSth = function (arr) {
  let newArr = arr.map(function (cur) {
    this.sum += cur;
    this.cnt++;
    return cur + 10;
  }, this) // this 指向例項物件
  return newArr;
}

let foo = new Foo();
let arr = [1, 2, 3];

console.log(foo.doSth(arr)); // [ 11, 12, 13 ]
console.log(foo.sum);// 6
console.log(foo.cnt);// 3
複製程式碼

一些小操作~

let arr = [1, 4, 9, 16];
let res = arr.map(Math.sqrt); // 傳入Math中sqrt得到陣列中每個元素的平方根
console.log(res); // [ 1, 2, 3, 4 ]
複製程式碼

總結

  • map 不修改呼叫它的原陣列本身(當然可以在 callback 執行時改變原陣列)

回撥函式不返回值時,最後新陣列的每個值都為undefined

  • this 的值最終相對於 callback 函式的可觀察性是依據this規則,也就是 this 指向問題
  • map 會返回一個新陣列

reduce 遍歷

定義:對陣列中的每個元素執行一個由您提供的 reducer 函式(升序執行),將其結果彙總為單個返回值。

先來介紹語法:

let res= array.reduce(callback(accumulator, currentValue, currentIndex, array), initialValue)
複製程式碼

callback:為陣列中每個元素執行的函式,該函式接收一至4個引數

accumulator 累計器 currentValue 當前值 currentIndex 當前索引 array 陣列 initialValue 作為第一次呼叫 callback函式時的第一個引數的值。 如果沒有提供初始值,則將使用陣列中的第一個元素。 在沒有初始值的空陣列上呼叫 reduce 將報錯。

簡單例子:

let arr = [3, 5, 7, 1, 2];
let res = arr.reduce(function (acc, cur, index, arr) {
  console.log(acc, cur, index, arr);
  return acc + cur;
}, 0)
// 0 3 0 [ 3, 5, 7, 1, 2 ]
// 3 5 1 [ 3, 5, 7, 1, 2 ]
// 8 7 2 [ 3, 5, 7, 1, 2 ]
// 15 1 3 [ 3, 5, 7, 1, 2 ]
// 16 2 4 [ 3, 5, 7, 1, 2 ]
console.log(res);
// 18
複製程式碼

看完上面程式碼,你可能還是蒙的,怎麼一下輸出這麼多,結合下面 gif 動圖再來理解一下吧:

疑難點,我想小夥伴們對於引數那麼多應該一下給看懵了,下面用一些小操作展示一下,並且提供一點自測題目加深鞏固~

let arr = [1, 2, 3, 4, 5];
let res = arr.reduce((acc, cur) => {
  return acc + cur;
}, 0)
console.log(res);// 15
複製程式碼

自測題:看看下面輸出什麼?

[1, 2, 3, 4].reduce((x, y) => console.log(x, y));
複製程式碼
  • A: 1 2 and 3 3 and 6 4
  • B: 1 2 and 2 3 and 3 4
  • C: 1 undefined and 2 undefined and 3 undefined and 4 undefined
  • D: 1 2 and undefined 3 and undefined 4
答案

答案: D

reducer 函式接收4個引數:

  1. Accumulator (acc) (累計器)
  2. Current Value (cur) (當前值)
  3. Current Index (idx) (當前索引)
  4. Source Array (src) (源陣列)

reducer 函式的返回值將會分配給累計器,該返回值在陣列的每個迭代中被記住,並最後成為最終的單個結果值。

reducer 函式還有一個可選引數initialValue, 該引數將作為第一次呼叫回撥函式時的第一個引數的值。如果沒有提供initialValue,則將使用陣列中的第一個元素。

在上述例子,reduce方法接收的第一個引數(Accumulator)是x, 第二個引數(Current Value)是y

在第一次呼叫時,累加器x1,當前值“y”2,列印出累加器和當前值:12

例子中我們的回撥函式沒有返回任何值,只是列印累加器的值和當前值。如果函式沒有返回值,則預設返回undefined。 在下一次呼叫時,累加器為undefined,當前值為“3”, 因此undefined3被列印出。

在第四次呼叫時,回撥函式依然沒有返回值。 累加器再次為 undefined ,當前值為“4”。 undefined4被列印出。


總結

  • 如果陣列為空且沒有提供 initialValue,會丟擲TypeError
  • 如果沒有提供 initialValuereduce 會從索引1的地方開始執行 callback 方法,跳過第一個索引。如果提供 initialValue ,從索引0開始。
  • acc為傳入函式的返回值,如果是 console.log,則返回預設值 undefined

filter()

定義:建立一個新陣列, 其包含通過所提供函式實現的測試的所有元素。

先來介紹語法:

let newArray = array.filter(function(currentValue, index, arr), thisArg)
複製程式碼

callback:為陣列中每個元素執行的函式,該函式接收一至三個引數

currentValue 陣列中正在處理的當前元素 index (可選) 陣列中正在處理的當前元素的索引 arr (可選) filter() 方法正在操作的陣列 thisArg(可選引數),當執行回撥函式callback,用作this值

簡單例子:

let arr = [1, 2, 3, 4, 5];

let newArr = arr.filter(function (cur, index) {
console.log(cur, index);
return cur % 2 == 0;
})
// 1 0
// 2 1
// 3 2
// 4 3
// 5 4
console.log(newArr); // [ 2, 4 ]
複製程式碼

關於 thisArg 相關可以參考上文 array.forEach() 方法 部分。

簡單來說,就是返回滿足條件的結果。

every()

定義:測試一個陣列內的所有元素是否都能通過某個指定函式的測試,它返回的是一個 Boolean 型別的值。

先來介紹語法:

array.every(function(currentValue, index, arr), thisArg)
複製程式碼

callback:為陣列中每個元素執行的函式,該函式接收一至三個引數

currentValue 陣列中正在處理的當前元素 index (可選) 陣列中正在處理的當前元素的索引 arr (可選) every() 方法正在操作的陣列 thisArg 可選引數,當執行回撥函式callback,用作this值

let res1 = [1, 2, 3, 4, 5].every(function (cur) {
  return cur > 10;
})
console.log(res1); // false

let res2 = [1, 2, 3, 4, 5].every(function (cur) {
return cur >= 1;
})
console.log(res2); // true

複製程式碼

關於 thisArg 相關可以參考上文 array.forEach() 方法 部分。

簡單來說,就是返回是否都能滿足特定條件的結果,用布林值返回。

some()

定義:測試陣列中是不是至少有1個元素通過了被提供的函式測試,它返回的是一個 Boolean 型別的值

先來介紹語法:

array.some(function(currentValue, index, arr), thisArg)
複製程式碼

callback:為陣列中每個元素執行的函式,該函式接收一至三個引數

currentValue 陣列中正在處理的當前元素 index (可選) 陣列中正在處理的當前元素的索引 arr (可選) some() 方法正在操作的陣列 thisArg (可選引數),當執行回撥函式callback,用作this值

let res1 = [1, 2, 3, 4, 5].some(function (cur) {
  return cur > 10;
})
console.log(res1); // false

let res2 = [1, 2, 3, 4, 5].some(function (cur) {
return cur === 1;
})
console.log(res2); // true
複製程式碼

關於 thisArg 相關可以參考上文 array.forEach() 方法 部分。

簡單來說,就是返回是否至少有1個滿足特定條件的結果,用布林值返回。

find 和 findIndex

該方法在ECMAScript 6規範中被加入,可能不存在於某些實現中。

定義:

find: 返回陣列中滿足提供的測試函式的第一個元素的值。否則返回 undefined

findIndex:陣列中通過提供測試函式的第一個元素的索引。否則,返回 -1

先來介紹語法:

let ele = array.find(function(elemnet, index, arr), thisArg)

let eleIndex = array.findIndex(function(elemnet, index, arr), thisArg)
複製程式碼

callback:為陣列中每個元素執行的函式,該函式接收一至三個引數

elemnet 陣列中正在處理的當前元素 index (可選) 陣列中正在處理的當前元素的索引 arr (可選) find方法正在操作的陣列 thisArg 可選引數,當執行回撥函式callback,用作this值

let res1 = [1, 2, 3, 4, 5].find(function (cur) {
  return cur > 2;
})
console.log(res1); // 3

let res2 = [1, 2, 3, 4, 5].findIndex(function (cur) {
return cur > 2;
})
console.log(res2); // 2
複製程式碼

keys 與 values 與 entries

定義:

  • keys() 方法返回一個包含陣列中每個索引鍵的 Array Iterator 物件。
  • values() 方法返回一個新的 Array Iterator 物件,該物件包含陣列每個索引的值
  • entries() 方法返回一個新的 Array Iterator 物件,該物件包含陣列中每個索引的鍵/值對
arr.keys()
arr.values()
arr.entries()
複製程式碼

簡單例子:

let arr = ['Chocolate', 'zhlll', 'lionkk'];

let itKeys = arr.keys();
let itVals = arr.values();
let itEntries = arr.entries();

for (let it of itKeys) {
console.log(it);
}
// 0
// 1
// 2
for (let it of itVals) {
console.log(it);
}
// Chocolate
// zhlll
// lionkk
for (let it of itEntries) {
console.log(it);
}
// [ 0, ‘Chocolate’ ]
// [ 1, ‘zhlll’ ]
// [ 2, ‘lionkk’ ]
複製程式碼


好了,到此關於陣列的遍歷方式基本上介紹完畢了,也許還有其它方法,但是萬變不離其宗,接下來我們將探究 改變原陣列 的方法。

改變原始陣列方法

看過我之前關於 Vue 資料劫持原始碼分析那篇部落格文章小夥伴應該知道,裡面就有提到了用裝飾者模式解決無法處理陣列問題。其中就有提到對於改變原始陣列的方法,這些需要繼續遞迴觀察。那麼,接下來,我們就來分別探討一下它們的使用:

sort()

至於為什麼我會排第一個,對,面試遇到過,當時我說對某手面試官說預設按照從小到大進行排序,通過學習後,我發現不是的...

先來介紹語法:

arr.sort([compareFunction])
複製程式碼

compareFunction 可選,用來指定按某種順序進行排列的函式。 如果省略,元素按照轉換為的字串的各個字元的Unicode位點進行排序。

否則,如果指明瞭compareFunction: 如果 compareFunction(a, b) 小於 0 ,那麼 a 會被排列到 b 之前; 如果 compareFunction(a, b) 等於 0 , a 和 b 的相對位置不變。 如果 compareFunction(a, b) 大於 0 , b 會被排列到 a 之前。

簡單例子:

let arr = [1, 10, 2, 5, 8, 3];

arr.sort(); // 預設
console.log(arr); // [ 1, 10, 2, 3, 5, 8 ]

arr.sort((a, b) => a - b); // 從小到大排序
console.log(arr); // [ 1, 2, 3, 5, 8, 10 ]

arr.sort((a, b) => b - a); // 從大到小排序
console.log(arr); // [ 10, 8, 5, 3, 2, 1 ]
複製程式碼

push()

類似棧、佇列的一些操作

注意push() 成功之後會返回陣列的長度。

let arr = [1,2];
let res = arr.push(100);
console.log(arr); // [ 1, 2, 100 ]
console.log(res); // 3
複製程式碼

pop()

類似棧、佇列的一些操作

let arr = [1, 2, 100];
let res = arr.pop();
console.log(arr); // [ 1, 2 ]
console.log(res); // 100
複製程式碼

shift()

類似棧、佇列的一些操作

let arr = [1, 2, 100];
let res = arr.shift();
console.log(arr); // [ 2, 100 ]
console.log(res); // 1
複製程式碼

unshift()

定義:將一個或多個元素新增到 陣列的開頭,並 (該方法修改原有陣列)

注意:該方法會返回該陣列的新長度

let arr = [1, 2, 100];
let res = arr.unshift(4, 5, 6);
console.log(arr); // [ 4, 5, 6, 1, 2, 100 ]
console.log(res); // 6
複製程式碼

reverse()

定義:將陣列中元素的位置顛倒,並返回該陣列。

let arr = [1, 2, 3];
arr.reverse();
console.log(arr);// [ 3, 2, 1 ]
複製程式碼

splice()

這個我放最後一個也是有原因的,它比其它幾個要更復雜一點,剛開始我也是花了老長時間才理解,而且原本一直與 split() 這些分不清楚。

定義:

通過刪除或替換現有元素或者原地新增新的元素來修改陣列,並以陣列形式返回被修改的內容。

array.splice(start,deleteCount,item1,.....,itemX)z
複製程式碼

start: 指定修改的開始位置(從0計數)

	1. 如果超出了陣列的長度,則從陣列末尾開始新增內容
	2. 如果是負值,則表示從陣列末位開始的第幾位(從-1計數,這意味著-n是倒數第n個元素,並且等價於array.length-n)
	3. 如果負數的絕對值大於陣列的長度,則表示開始位置為第0位
複製程式碼

deleteCount(可選) : 整數,表示要移除的陣列元素個數

	1. 如果 deleteCount 大於 start 之後的元素的總數,則從 start 後面的元素都將被			刪除(含第 start 位)
	2. 如果 deleteCount 被省略了,或者它的值大於等於array.length - start(也就是		   說,如果它大於或者等於start之後的所有元素的數量),那麼start之後陣列的所有元素都會被刪除。
	3. 如果 deleteCount 是 0 或者負數,則不移除元素。這種情況下,至少應新增一個新		   元素。
複製程式碼

item1, item2, ...(可選)

要新增進陣列的元素,從start 位置開始。如果不指定,則 splice() 將只刪除陣列元素。

從第2位開始插入“Chocolate”

let arr = ['one', 'two', 'three'];

arr.splice(2, 0, ‘Chocolate’);
console.log(arr);// [ ‘one’, ‘two’, ‘Chocolate’, ‘three’ ]
複製程式碼

從第 2 位開始刪除 1 個元素,然後插入“Chocolate”

let arr = ['one', 'two', 'three'];

arr.splice(2, 1, ‘Chocolate’);
console.log(arr);// [ ‘one’, ‘two’, ‘Chocolate’ ]
複製程式碼

主流的還是這7個方法,對於改變原陣列還有 fill()copyWithin() 方法,小夥伴們可以繼續研究~


陣列的對映

Array.map()方法

上文已經介紹

Array.from()方法

定義:通過在每個陣列項上使用 callback 呼叫結果來建立一個新陣列。

先來介紹語法:

 Array.from(Array,callback(currentValue, index, arr))
複製程式碼

簡單例子:

let arr = [1, 2, 3];

let newArr = Array.from(arr, function (cur) {
return cur + 10;
})
console.log(newArr);// [ 11, 12, 13 ]
複製程式碼


陣列的連線

Array.concat() 方法

array.concat(array1[, array2, ...]) 將一個或多個陣列連線到原始陣列。如下所示,連線兩個陣列:

let arrA = [1, 2, 3];
let arrB = [4, 5, 6];
let ans = arrA.concat(arrB);
console.log(ans);// [ 1, 2, 3, 4, 5, 6 ]
複製程式碼

展開操作符

let arrA = [1, 2, 3];
let arrB = [4, 5, 6];
let ans = [...arrA, ...arrB];
console.log(ans);// [ 1, 2, 3, 4, 5, 6 ]
複製程式碼

獲取陣列的片段

Array.slice() 方法

定義: 返回一個新的陣列物件,這一物件是一個由 beginend 決定的原陣列的淺拷貝(包括 begin,不包括 end)——原始陣列不會被改變。

先介紹語法:

arr.slice([begin[, end]])
複製程式碼

begin (可選)

  1. 提取起始處的索引(從 0 開始),從該索引開始提取原陣列元素。
  2. 如果該引數為負數,則表示從原陣列中的倒數第幾個元素開始提取
  3. slice(-2) 表示提取原陣列中的倒數第二個元素到最後一個元素(包含最後一個元素)
  4. 如果省略 begin,則 slice 從索引 0 開始。
  5. 如果 begin 大於原陣列的長度,則會返回空陣列。

end (可選)

  1. slice(1,4) 會提取原陣列中從第二個元素開始一直到第四個元素的所有元素 (索引為 1, 2, 3的元素)
  2. 如果該引數為負數, 則它表示在原陣列中的倒數第幾個元素結束抽取。
  3. 如果 end 被省略,則 slice 會一直提取到原陣列末尾。
  4. 如果 end 大於陣列的長度,slice 也會一直提取到原陣列末尾。
let fruits = ['Banana', 'Orange', 'Lemon', 'Apple', 'Mango'];
let res = fruits.slice(1, 3);
let res1 = fruits.slice(1);
let res2 = fruits.slice(-1);
let res3 = fruits.slice(0, -1);
console.log(res); // [ 'Orange', 'Lemon' ]
console.log(res1);// [ 'Orange', 'Lemon', 'Apple', 'Mango' ]
console.log(res2);// [ 'Mango' ]
console.log(res3);// [ 'Banana', 'Orange', 'Lemon', 'Apple' ]
複製程式碼

轉換陣列

join()

定義:

將一個陣列(或一個類陣列物件)的所有元素連線成一個字串並返回這個字串。如果陣列只有一個專案,那麼將返回該專案而不使用分隔符。

語法:

arr.join(separator)
複製程式碼

簡單例子:

let arr = ['one', 'two', 'three'];
let res = arr.join('^');
let res1 = arr.join('&');

console.log(res); // onetwothree
console.log(res1); // one&two&three
複製程式碼

split()

定義:

使用指定的分隔符字串將一個 String 物件分割成子字串陣列,以一個指定的分割字串來決定每個拆分的位置。

語法:

str.split([separator[, limit]])
複製程式碼
const str = 'The best Chocolate';

const words = str.split(’ ');
console.log(words); // [ ‘The’, ‘best’, ‘Chocolate’ ]
console.log(words[2]); // Chocolate
複製程式碼

toString()

定義:

返回一個字串,表示指定的陣列及其元素。

當一個陣列被作為文字值或者進行字串連線操作時,將會自動呼叫toString 方法。

語法:

arr.toString()
複製程式碼
let arr = ['one', 'two', 'three'];
console.log(arr.toString()); // one,two,three
複製程式碼

陣列的扁平化

flat()

定義:

按照一個可指定的深度遞迴遍歷陣列,並將所有元素與遍歷到的子陣列中的元素合併為一個新陣列返回。

語法:

var newArray = arr.flat([depth])
複製程式碼

引數

depth 可選 指定要提取巢狀陣列的結構深度,預設值為 1。 返回值 一個包含將陣列與子陣列中所有元素的新陣列。

const arr1 = [0, 1, 2, [3, 4]];
console.log(arr1.flat()); // [ 0, 1, 2, 3, 4 ]

const arr2 = [0, 1, 2, [[[3, 4]]]];
console.log(arr2.flat(2)); // [ 0, 1, 2, [ 3, 4 ] ]
複製程式碼


備忘錄

相關文章