JavaScript 數組完全手冊

在編程世界中,數組是指元素的集合。數組將數據作爲元素進行存儲,並在需要時將其取出。

在支持數組的編程語言中廣泛地採用了這個數據結構。

這個手冊會介紹 JavaScript 數組的所有知識。你將會學到複雜數據處理、解構、常用數組方法等內容。

我爲什麼寫這篇文章

網上已經有很多介紹 JavaScript 數組的優秀文章,那我爲什麼還要寫一篇相同主題的文章呢?動機是什麼?

多年來,通過和學員的交流,我意識到大多數初學者都需要這樣一個教程:通過示例從頭到尾徹底地介紹數組。

所以我決定編寫這樣一篇包含大量示例的文章。如果你是初學者,希望這篇文章對你有所幫助。

不過,這個手冊也能幫助有經驗的開發者梳理知識。我在寫作這篇文章的過程中,也重新學習了相關知識。我們開始吧。

JavaScript 中的數組是什麼

在 JavaScript 中,一對方括號([]) 表示一個數組,其中的所有元素以逗號(,) 分隔。

在 JavaScript 中,數組可以是任意類型元素組成的集合。這意味着,創建一個數組,它的元素類型可以是 String、Boolean、Number、Object,甚至是另一個數組。

示例中的數組包含 4 個元素,類型分別是:Number、Boolean、String 和 Object。

const mixedTypedArray = [100, true, 'freeCodeCamp'{}];

元素在數組中的位置稱爲索引(index),JavaScript 中的數組索引是從 0 開始計數的,每加入一個新元素,其對應的索引加 1。

例如,上面的數組中,100 這個元素的位置是 索引 0true 的位置是索引 1'freeCodeCamp' 的位置是索引 2,以此類推。

數組中的元素數量決定了數組長度(length)。比如說,上面的數組長度是 4。

有趣的是,JavaScript 數組的長度是可變的,你可以隨時將它指定爲一個非負整數值。我們稍後會學習更多相關知識。

如何創建數組

在 JavaScript 中有多種方式可以創建數組,最直接的方式是把數組字面量賦值給一個變量。

const salad = ['🍅''🍄''🥦''🥒''🌽''🥕''🥑'];

也可以使用 Array 構造函數來創建數組。

const salad = new Array('🍅''🍄''🥦''🥒''🌽''🥕''🥑');

注意:new Array(2) 會創建一個長度爲 2 的空數組,然而 new Array(1,2) 則會創建一個包含兩個元素(1 和 2)的數組。

另外,Array.of()Array.from() 方法,以及展開運算符(...)也可以創建數組。我們後面會學習它們。

如何訪問數組元素

可以使用數組索引來獲取數組元素,訪問數組元素需要用到方括號 []

const element = array[index];

根據使用場景,你可能需要一個一個地訪問數組元素或者使用循環來遍歷。

可以像這樣使用索引來訪問數組元素:

const salad = ['🍅''🍄''🥦''🥒''🌽''🥕''🥑'];
salad[0]; // '🍅'
salad[2]; // '🥦'
salad[5]; // '🥕'

也可以利用數組長度(length 屬性)值,反向遍歷訪問數組元素。

const salad = ['🍅''🍄''🥦''🥒''🌽''🥕''🥑'];
const len = salad.length;
salad[len - 1]; // '🥑'
salad[len - 3]; // '🌽'

可以使用一般的 for 循環或 forEach 方法來遍歷數組,也可以使用其它方式來遍歷。

const salad = ['🍅''🍄''🥦''🥒''🌽''🥕''🥑'];

for(let i=0; i<salad.length; i++) {
  console.log(`Element at index ${i} is ${salad[i]}`);
}

結果如下:

如何向數組中添加元素


可以使用 push() 方法向數組中插入一個元素,它會將元素追加到數組的末尾。我們往沙拉中加入一些花生:

const salad = ['🍅''🍄''🥦''🥒''🌽''🥕''🥑'];
salad.push('🥜');

現在沙拉數組看起來像這樣:

["🍅", "🍄", "🥦", "🥒", "🌽", "🥕", "🥑", "🥜"]

注意,push() 方法會把元素追加到數組末尾,如果想要在數組頭部插入一個元素,需要使用 unshift() 方法。

const salad = ['🍅''🍄''🥦''🥒''🌽''🥕''🥑'];
salad.unshift('🥜');

現在沙拉數組看起來像這樣:

["🥜", "🍅", "🍄", "🥦", "🥒", "🌽", "🥕", "🥑"]

如何移除數組元素

移除單個數組元素的最簡單方式是使用 pop() 方法。每次調用 pop() 方法,都會移除數組末尾的那個元素。pop() 方法的返回值是那個被移除的元素,這個方法會改變原始數組。

const salad = ['🍅''🍄''🥦''🥒''🌽''🥕''🥑'];
salad.pop(); // 🥑

console.log(salad); // ['🍅''🍄''🥦''🥒''🌽''🥕']

使用 shift() 方法可以移除數組頭部的一個元素。與 pop() 方法類似,shift() 方法會返回那個被移除的元素,並且會改變原始數組。

const salad = ['🍅''🍄''🥦''🥒''🌽''🥕''🥑'];
salad.shift(); // 🍅

console.log(salad); // ['🍄''🥦''🥒''🌽''🥕''🥑'];

如何克隆數組

可以使用 slice() 方法來克隆數組。注意,slice() 方法不改變原始數組,而是創建一個原始數組的淺拷貝副本。

const salad = ['🍅''🍄''🥦''🥒''🌽''🥕''🥑'];
const saladCopy = salad.slice();

console.log(saladCopy); // ['🍅''🍄''🥦''🥒''🌽''🥕''🥑']

salad === saladCopy; // returns false

也可以使用展開運算符來創建數組副本,我們很快會學到。

如何判斷某個值是不是數組

可以使用 Array.isArray(value) 方法來判斷某個值是不是數組,如果傳入的值是一個數組的話,它會返回 true。

Array.isArray(['🍅''🍄''🥦''🥒''🌽''🥕''🥑']); // returns true
Array.isArray('🍅'); // returns false
Array.isArray({ 'tomato''🍅'}); // returns false
Array.isArray([]); // returns true

數組解構

ECMAScript 6(ES6)提供了一些新語法,可以一次性從數組中獲取多個元素並賦值給多個變量。它有助於保持代碼簡潔明瞭。這個新語法被稱爲解構語法。

下面是使用解構語法從數組中獲取多個元素的例子:

let [tomato, mushroom, carrot] = ['🍅''🍄''🥕'];

現在就可以使用這些變量了:

console.log(tomato, mushroom, carrot); // Output, 🍅 🍄 🥕

如果不使用解構語法的話,代碼會是這樣:

let vegetables = ['🍅''🍄''🥕'];
let tomato = vegetables[0];
let mushroom= vegetables[1];
let carrot= vegetables[2];

所以,解構語法能夠有助於減少代碼量、極大地提高生產力。

如何爲變量指定默認值

使用解構語法時,可以爲變量指定默認值,當數組中沒有對應的元素或者元素的值爲 undefined 時,就會使用默認值。

下面的例子中,我們爲 mushroom 變量指定了一個默認值。

let [tomato , mushroom = '🍄'] = ['🍅'];
console.log(tomato); // '🍅'
console.log(mushroom ); // '🍄'

如何跳過某個數組元素

使用解構獲取數組元素時,可以跳過某個元素。比如說,你可能只關注數組的部分元素,這時候這個語法就派上用場了。

下面的例子中,我們跳過了 “蘑菇” 元素。注意表達式左邊變量聲明中的空格。

let [tomato, , carrot] = ['🍅''🍄''🥕'];

console.log(tomato); // '🍅'
console.log(carrot); // '🥕'

嵌套數組解構

JavaScript 中,數組是可以嵌套的。這意味着一個數組的元素可以是另一個數組。數組可以嵌套任意深度。

例如,我們創建一個嵌套數組 fruits,其元素包含一些水果和一個 “蔬菜” 數組。

let fruits = ['🍈''🍍''🍌''🍉'['🍅''🍄''🥕']];

要如何獲取以上數組中的 '🥕' 呢?同樣的,不使用解構的話,可以這樣做:

const veg = fruits[4]; // returns the array ['🍅''🍄''🥕']
const carrot = veg[2]; // returns '🥕'

或者,也可以使用簡寫語法:

fruits[4][2]; // returns '🥕'

還可以使用解構語法:

let [,,,,[,,carrot]] = ['🍈''🍍''🍌''🍉'['🍅''🍄''🥕']];

如何使用展開語法(Spread Syntax)和剩餘參數(Rest Parameter)

從 ES6 開始,通過 ...(連續的三個點)可以在數組解構中使用展開語法和剩餘參數。

如何使用剩餘參數

通過剩餘參數,可以將剩下的元素映射到一個新的數組中。剩餘參數必須是解構語法中的最後一個變量。

下面的例子中,我們把數組的前兩個參數分別映射到了 tomato 和 mushroom 變量中,剩下的元素則使用 ... 映射到了 rest 變量中。rest 是一個新數組,其中包含了剩下的元素。

const [tomato, mushroom, ...rest] = ['🍅''🍄''🥦''🥒''🌽''🥕''🥑'];

console.log(tomato); // '🍅'
console.log(mushroom); // '🍄'
console.log(rest); // ["🥦""🥒""🌽""🥕""🥑"]

如何使用展開運算符

使用展開運算符,可以這樣來克隆現有的數組:

const salad = ['🍅''🍄''🥦''🥒''🌽''🥕''🥑'];

const saladCloned = [...salad];
console.log(saladCloned); // ["🍅""🍄""🥦""🥒""🌽""🥕""🥑"]

salad === saladCloned // false

解構的使用場景

我們一起來看看數組解構、展開運算符和剩餘參數的一些激動人心的使用場景。

使用解構交換變量值

使用數組解構語法可以很輕鬆地交換兩個變量的值。

let first = '😔';
let second = '🙂';
[first, second] = [second, first];

console.log(first);  // '🙂'
console.log(second); // '😔'

合併數組

我們可以通過合併兩個數組的所有元素來創建一個新數組(不改變原始數組)。假設現在有兩個數組——一個包含一些笑臉,另一個包含一些蔬菜。

const emotion = ['🙂''😔'];
const veggies = ['🥦''🥒''🌽''🥕'];

現在,我們要把它們合併成一個新數組。

const emotionalVeggies = [...emotion, ...veggies];
console.log(emotionalVeggies); // ["🙂""😔""🥦""🥒""🌽""🥕"]

JavaScript 數組方法

到目前爲止,我們已經瞭解了一些數組屬性和方法。我們做一個簡單的回顧:

現在我們將通過示例來學習其它重要的數組方法。

如何創建數組、刪除數組元素、更新數組元素以及訪問數組元素

這一節,我們要學習用於創建新數組、移除數組元素及清空數組、訪問數組元素等操作的方法。

concat() 方法

concat() 方法可以將多個數組合並在一起並返回合併後的數組。這是一個不可變方法,意味着它不會改變現有的數組。

拼接兩個數組:

const first = [1, 2, 3];
const second = [4, 5, 6];

const merged = first.concat(second);

console.log(merged); // [1, 2, 3, 4, 5, 6]
console.log(first); // [1, 2, 3]
console.log(second); // [4, 5, 6]

使用 concat() 方法也可以拼接兩個以上的數組。我們可以這樣拼接任意數量的數組:

array.concat(arr1, arr2,..,..,..,arrN);

示例如下:

const first = [1, 2, 3];
const second = [4, 5, 6];
const third = [7, 8, 9];

const merged = first.concat(second, third);

console.log(merged); // [1, 2, 3, 4, 5, 6, 7, 8, 9]

join() 方法

join() 方法使用一個分隔符將數組的所有元素拼接成一個字符串,並返回這個字符串。默認的分隔符是逗號(,)。

const emotions = ['🙂''😍''🙄''😟'];

const joined = emotions.join();
console.log(joined); // "🙂,😍,🙄,😟"

可以傳入一個自定義分隔符用於拼接數組元素。下面是一個使用自定義分隔符拼接數組元素的例子:

const joined = emotions.join('<=>');
console.log(joined); // "🙂<=>😍<=>🙄<=>😟"

在空數組上調用 join() 方法,返回一個空字符串:

[].join() // returns ""

fill() 方法

fill() 方法使用一個固定值填充數組。可以使用這個固定值填充整個數組,也可以只覆蓋選定的元素。注意,fill() 方法會改變原始數組。

const colors = ['red''blue''green'];

colors.fill('pink');
console.log(colors); // ["pink""pink""pink"]

下面是一個使用 fill() 方法覆蓋數組的最後兩個元素的例子:

const colors = ['red''blue''green'];

colors.fill('pink', 1,3); // ["red""pink""pink"]

這個例子中,fill() 方法的第一個參數是用來填充數組的值,第二個參數是替換的起始索引(從 0 開始計算),最後一個參數是終止索引(最大值可以是 colors.length)。

請查看這個 Twitter 主題以瞭解 fill() 方法的實際用法。

你也可以查看這個示例項目:https://github.com/atapas/array-fill-color-cards。

includes() 方法

可以使用 includes() 方法來判斷一個數組中是否包含某個元素,如果包含則返回 true,否則返回 false

const names = ['tom''alex''bob''john'];

names.includes('tom'); // returns true
names.includes('july'); // returns false

indexOf() 方法

可以使用 indexOf() 方法找到某個元素在數組中的索引位置。它返回這個元素在數組中首次出現的索引,如果沒有找到這個元素則返回 -1

const names = ['tom''alex''bob''john'];

names.indexOf('alex'); // returns 1
names.indexOf('rob'); // returns -1

還有一個 lastIndexOf() 方法,可以找出某個元素在數組中最後出現的位置。與 indexOf() 類似,lastIndexOf() 在找不到這個元素時也返回 -1

const names = ['tom''alex''bob''tom'];

names.indexOf('tom'); // returns 0
names.lastIndexOf('tom'); // returns 3

reverse() 方法

顧名思義,reverse() 方法將數組中元素的位置顛倒,最後一個元素變成第一個、第一個元素變成最後一個。

const names = ['tom''alex''bob'];

names.reverse(); // returns ["bob""alex""tom"]

reverse() 方法會改變原始數組。

sort() 方法

sort() 方法可能是最常用的數組方法之一。sort() 方法默認會把元素轉換爲字符串再對它們進行排序。默認的排序方式是升序排列。sort() 方法會改變原始數組。

const names = ['tom''alex''bob'];

names.sort(); // returns ["alex""bob""tom"]

sort() 方法接收一個可選的比較器函數作爲參數,可以編寫一個比較器函數傳入 sort() 方法來覆蓋默認的排序行爲。

假設現在有一個數字數組,我們使用比較器函數將它按升序和降序排序:

const numbers = [23, 5, 100, 56, 9, 13, 37, 10, 1]

首先,調用 sort() 方法,並觀察結果:

numbers.sort();

現在,排序後的數組爲 [1, 10, 100, 13, 23, 37, 5, 56, 9]。這並不是我們預期的結果。得到這個結果是因爲 sort() 方法默認會將元素轉換爲字符串,再基於字符串諸個字符對應的 UTF-16 編碼值進行比較。

爲了解決這個問題,我們編寫一個比較器函數。這是用於升序排序的:

function ascendingComp(a, b){
  return (a-b);
}

把比較器函數傳入 sort() 方法:

numbers.sort(ascendingComp); // retruns [1, 5, 9, 10, 13, 23, 37, 56, 100]

/* 

也可以使用行內函數:

numbers.sort(function(a, b) {
  return (a-b);
});

或者使用箭頭函數的寫法:

numbers.sort((a, b) =(a-b));

*/

降序排序:

numbers.sort((a, b) =(b-a));

查看這個 GitHub 倉庫以獲取更多排序示例和技巧:https://github.com/atapas/js-array-sorting。

splice() 方法

splice() 方法可以幫助你向數組中添加元素、更新數組元素以及移除數組元素。剛開始接觸這個方法可能會令人困惑,不過只要你理解了它的正確用法,就能夠掌握。

splice() 方法的主要目標是從數組中移除元素。它會返回由被移除的元素組成的數組,並且會改變原始數組。你也可以用它來向數組中添加元素或者替換數組中的元素。

使用 splice() 方法向數組中添加一個元素,需要傳入插入的目標位置、從目標位置算起想要刪除的元素數量以及要插入的元素。

下面的例子中,我們在索引爲 1 的位置上插入了一個元素 zack,沒有刪除任何元素。

const names = ['tom''alex''bob'];

names.splice(1, 0, 'zack');

console.log(names); // ["tom""zack""alex""bob"]

看看下面的例子,我們移除了索引 2 位置之後的一個元素(即第三個元素),並添加了一個元素 zacksplice() 方法返回一個由移除掉的元素——bob——組成的數組。

const names = ['tom''alex''bob'];

const deleted = names.splice(2, 1, 'zack');

console.log(deleted); // ["bob"]
console.log(names); // ["tom""alex""zack"]

查看這個 Twitter 主題以瞭解如何使用 splice() 方法清空數組。

靜態數組方法

在 JavaScript 中,數組有三個靜態方法。我們已經討論過 Array.isArray(),接下來要探討其餘兩個方法。

Array.from() 方法

假設有以下 HTML 代碼片段,其中包含一個 div 和一些列表元素:

<div id="main">
  <ul>
    <ol type="1">
      <li>...</li>
      <li>...</li>
      <li>...</li>
      <li>...</li>
      <li>...</li>
      <li>...</li>
      <li>...</li>
      <li>...</li>
      <li>...</li>
      <li>...</li>
    </ol>
  </ul> 
</div>

我們使用 getElementsByTagName() 方法獲取這些列表元素。

document.getElementsByTagName('li');

它返回如下 HTMLCollection 對象:

HTMLCollection 是類數組對象

它和數組類似,我們試着使用 forEach 來遍歷它:

document.getElementsByTagName('li').forEach(() ={
 // Do something here..
})

猜猜會輸出什麼?會報出以下錯誤:

在類數組對象上調用 forEach 發生錯誤

爲什麼會這樣?這是因爲 HTMLCollection 並不是數組,而是 類數組 對象,所以不能使用 forEach 來遍歷它。

其原型(proto)是 Object

這裏就需要用到 Array.from() 方法了,Array.from() 能將類數組對象轉換爲數組,進而能夠在它上面執行所有數組操作。

const collection = Array.from(document.getElementsByTagName('li'))

這裏的 collection 是一個數組:

其原型爲 Array

Array.of() 方法

Array.of() 可以使用任意數量任意類型的元素創建一個新數組。

Array.of(2, false, 'test'{'name''Alex'})

輸出如下:

Array.of() 方法的輸出結果

數組迭代器方法

現在我們要學習數組迭代器方法。這些方法在執行數組迭代、計算、做判斷、過濾元素等操作時很有用。

到目前爲止,我們還沒見過對象數組的示例。在這一節,我們將會使用下面的對象數組來解釋和演示這些迭代器方法。

這個數組包含了一些訂閱各種付費課程的學生的信息:

let students = [
   {
      'id': 001,
      'f_name''Alex',
      'l_name''B',
      'gender''M',
      'married': false,
      'age': 22,
      'paid': 250,  
      'courses'['JavaScript''React']
   },
   {
      'id': 002,
      'f_name''Ibrahim',
      'l_name''M',
      'gender''M',
      'married': true,
      'age': 32,
      'paid': 150,  
      'courses'['JavaScript''PWA']
   },
   {
      'id': 003,
      'f_name''Rubi',
      'l_name''S',
      'gender''F',
      'married': false,
      'age': 27,
      'paid': 350,  
      'courses'['Blogging''React''UX']
   },
   {
      'id': 004,
      'f_name''Zack',
      'l_name''F',
      'gender''M',
      'married': true,
      'age': 36,
      'paid': 250,  
      'courses'['Git''React''Branding']
   } 
];

讓我們開始吧。所有數組迭代器方法都接收一個函數作爲參數,需要在這個函數中聲明迭代邏輯。

filter() 方法

filter() 方法用所有滿足過濾條件的元素來創建一個新數組。我們要找出女學生,所以過濾條件應該是 gender === 'F'

const femaleStudents = students.filter((element, index) ={
  return element.gender === 'F';
})

console.log(femaleStudents);

輸出如下:

結果是正確的,名爲 Rubi 的學生是目前唯一的女學生。

map()方法

map() 方法遍歷整個數組,依次對數組元素執行回調函數並用這些返回值創建一個新數組。我們將會創建一個由 students 數組中所有學生的全名組成的新數組。

const fullNames = students.map((element, index) ={
  return {'fullName': element['f_name'] + ' ' + element['l_name']}
});

console.log(fullNames);

輸出如下:

這裏我們可以看到由包含 fullName 屬性的對象組成的數組,fullName 是由 student 對象的 f_namel_name 屬性計算得到的。

reduce() 方法

reduce() 方法對每個數組元素執行 reducer 函數,並將其結果彙總爲單個返回值。我們將會在 students 數組中應用一個 reducer 函數來計算所有學生支付的總額。

const total = students.reduce(
   (accumulator, student, currentIndex, array) ={
      accumulator = accumulator + student.paid;
      return (accumulator);
   }, 
0);

console.log(total); // 1000

在上面的代碼中,

some() 方法

some() 方法返回一個布爾值(true/false),其返回值取決於數組中是否至少有一個元素符合回調函數中的判斷條件。我們來看看是否有學生的年齡小於 30 歲。

let hasStudentBelow30 = students.some((element, index) ={
  return element.age < 30;
});

console.log(hasStudentBelow30); // true

是的,我們看到至少有一個學生的年齡是小於 30 歲的。

find() 方法

使用 some() 方法,我們已經看到有一個 30 歲以下的學生。讓我們找出這個學生。

爲此,我們會用到 find() 方法,它會返回數組中第一個滿足判斷條件的元素。

還有另一個相關的方法 findIndex(),這個方法返回我們使用 find() 方法找到的元素的索引,如果沒有符合條件的元素則返回 -1

下面的例子中,我們向 find() 方法中傳入了一個函數用來判斷學生的年齡,它會返回滿足判斷條件的學生。

const student = students.find((element, index) ={
  return element.age < 30;
});

console.log(student);

輸出如下:

可以看到,他就是 22 歲的 Alex,我們找到他了。

every() 方法

every() 方法檢查是否數組的每個元素都滿足給定的判斷條件。讓我們檢查一下是不是所有學生都訂閱了至少兩門課程。

const atLeastTwoCourses = students.every((elements, index) ={
  return elements.courses.length >= 2;
});

console.log(atLeastTwoCourses); // true

正如預期,我們看到結果爲 true

提案中的方法

截至 2021 年 5 月,ECMAScript 提案中有一個新的數組方法,即 at() 方法。

at() 方法

提案中的 at() 方法可以讓你使用負數索引來訪問數組元素(譯註:使用負數索引即從數組末尾開始訪問元素,-1 表示最後一個元素、-2 表示倒數第二個元素…… 以此類推)。截至目前,這個方法還不可用。現在只能使用正數索引從數組開頭訪問元素。

目前想從數組末尾開始訪問數組元素要藉助 length 屬性。通過引入 at() 方法,就可以在單個方法裏面使用正數索引或者負數索引來訪問元素。

const junkFoodILove = ['🥖''🍔''🍟''🍕''🌭''🥪''🌮''🍿'];

junkFoodILove.at(0); // 🥖
junkFoodILove.at(3); // 🍕
junkFoodILove.at(-1); // 🍿
junkFoodILove.at(-5); // 🍕
junkFoodILove.at(-8); // 🥖
junkFoodILove.at(10); // undefined

這是一個簡單示例:

JavaScript at() 方法示例

at() 方法加入 JavaScript 語言之前,你可以使用這個 polyfill 來獲得它的功能。查看這個 GitHub 倉庫以獲取 at() 方法的示例:https://github.com/atapas/js-array-at-method。

結束之前......

希望你覺得這篇文章有價值,也希望它能夠幫助你更好地理解 JavaScript 數組。請多多練習文中的示例,以便更好地掌握它們。你可以在我的 GitHub 倉庫中找到所有代碼示例。

保持聯繫,我平時活躍在 Twitter (@tapasadhikary),歡迎關注我。

原文鏈接:https://www.freecodecamp.org/news/the-javascript-array-handbook/

作者:TAPAS ADHIKARY

譯者:Humilitas

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/IrZIFG0HEOPb3jCfogMkUw