陣列

在本課程中,我們將瞭解陣列——一種在單個變數名下儲存資料列表的巧妙方法。在這裡,我們探討了它為什麼有用,然後學習如何建立陣列、檢索、新增和刪除陣列中儲存的項,以及更多內容。

預備知識 理解 HTMLCSS 基礎知識。熟悉基本資料型別,例如數字和字串,如之前的課程所述。
學習成果
  • 什麼是陣列——一個包含變數列表的結構。
  • 陣列的語法——[a, b, c] 和訪問器語法 myArray[x]
  • 使用 myArray[x] = y 修改陣列值。
  • 使用常見的屬性和方法運算元組,例如 lengthpush()pop()join()split()
  • 高階陣列方法,例如 forEach()map()filter()

什麼是陣列?

陣列通常被描述為“類列表物件”;它們基本上是包含儲存在列表中的多個值的單個物件。陣列物件可以儲存在變數中,並以與其他任何型別的值大致相同的方式處理,不同之處在於我們可以單獨訪問列表中的每個值,並使用該列表做一些超級有用和高效的事情,例如遍歷它並對每個值執行相同的操作。也許我們有一個儲存在陣列中的一系列產品項及其價格,我們希望遍歷所有這些項並將它們列印在發票上,同時將所有價格加起來並在底部打印出總價。

如果我們沒有陣列,我們就必須將每個項儲存在單獨的變數中,然後單獨為每個項呼叫執行列印和新增的程式碼。這將大大延長編寫時間,效率更低,並且更容易出錯。如果我們要將 10 個項新增到發票中,就已經很煩人了,但是 100 個項,或者 1000 個呢?我們將在本文後面回到這個例子。

與之前的文章一樣,讓我們透過在瀏覽器開發者控制檯中輸入一些示例來學習陣列的真正基礎知識。

注意:Scrimba 的旁註:陣列簡介 scrim MDN 學習合作伙伴 提供了一個有用的互動式陣列介紹,其中包含示例演練和測試您知識的挑戰。

建立陣列

陣列由方括號和逗號分隔的項組成。

  1. 假設我們想將一個購物清單儲存在陣列中。將以下程式碼貼上到控制檯中

    js
    const shopping = ["bread", "milk", "cheese", "hummus", "noodles"];
    console.log(shopping);
    
  2. 在上面的例子中,每個項都是一個字串,但在陣列中我們可以儲存各種資料型別——字串、數字、物件,甚至其他陣列。我們也可以在單個數組中混合資料型別——我們不必將自己限制在一個數組中只儲存數字,在另一個數組中只儲存字串。例如

    js
    const sequence = [1, 1, 2, 3, 5, 8, 13];
    const random = ["tree", 795, [0, 1, 2]];
    
  3. 在繼續之前,建立一些示例陣列。

查詢陣列的長度

您可以透過與查詢字串長度(以字元為單位)完全相同的方式來查詢陣列的長度(其中有多少項)——透過使用length屬性。嘗試以下操作

js
const shopping = ["bread", "milk", "cheese", "hummus", "noodles"];
console.log(shopping.length); // 5

訪問和修改陣列項

陣列是索引集合。陣列中的項從零開始編號。這個數字稱為項的索引。因此,第一個項的索引是 0,第二個項的索引是 1,依此類推。您可以使用方括號表示法並提供項的索引來訪問陣列中的單個項,就像您訪問字串中的字母一樣。

  1. 在控制檯中輸入以下內容

    js
    const shopping = ["bread", "milk", "cheese", "hummus", "noodles"];
    console.log(shopping[0]);
    // returns "bread"
    
  2. 您還可以透過為單個數組項賦予新值來修改陣列中的項。試試這個

    js
    const shopping = ["bread", "milk", "cheese", "hummus", "noodles"];
    shopping[0] = "tahini";
    console.log(shopping);
    // shopping will now return [ "tahini", "milk", "cheese", "hummus", "noodles" ]
    

    注意:我們之前說過,但只是提醒一下——JavaScript 陣列的索引從零開始!

  3. 請注意,陣列中的陣列稱為多維陣列。您可以透過連結兩組方括號來訪問陣列中(該陣列本身在另一個數組中)的項。例如,要訪問 random 陣列中(見上一節)的第三個項中的某個項,我們可以這樣做

    js
    const random = ["tree", 795, [0, 1, 2]];
    random[2][2];
    
  4. 在繼續之前,嘗試對您的陣列示例進行更多修改。多玩玩,看看哪些有效,哪些無效。

查詢陣列中項的索引

如果您不知道某個項的索引,可以使用indexOf()方法。indexOf() 方法將項作為引數,並返回該項的索引,如果該項不在陣列中,則返回-1

js
const birds = ["Parrot", "Falcon", "Owl"];
console.log(birds.indexOf("Owl")); //  2
console.log(birds.indexOf("Rabbit")); // -1

新增項

要向陣列末尾新增一個或多個項,我們可以使用push()。請注意,您需要包含一個或多個要新增到陣列末尾的項。

js
const cities = ["Manchester", "Liverpool"];
cities.push("Cardiff");
console.log(cities); // [ "Manchester", "Liverpool", "Cardiff" ]
cities.push("Bradford", "Brighton");
console.log(cities); // [ "Manchester", "Liverpool", "Cardiff", "Bradford", "Brighton" ]

方法呼叫完成後,將返回陣列的新長度。如果您想將新的陣列長度儲存在變數中,可以這樣做

js
const cities = ["Manchester", "Liverpool"];
const newLength = cities.push("Bristol");
console.log(cities); // [ "Manchester", "Liverpool", "Bristol" ]
console.log(newLength); // 3

要向陣列開頭新增項,請使用unshift()

js
const cities = ["Manchester", "Liverpool"];
cities.unshift("Edinburgh");
console.log(cities); // [ "Edinburgh", "Manchester", "Liverpool" ]

刪除項

要從陣列中刪除最後一個項,請使用pop()

js
const cities = ["Manchester", "Liverpool"];
cities.pop();
console.log(cities); // [ "Manchester" ]

pop() 方法返回被刪除的項。要將該項儲存在新變數中,您可以這樣做

js
const cities = ["Manchester", "Liverpool"];
const removedCity = cities.pop();
console.log(removedCity); // "Liverpool"

要從陣列中刪除第一個項,請使用shift()

js
const cities = ["Manchester", "Liverpool"];
cities.shift();
console.log(cities); // [ "Liverpool" ]

如果您知道某個項的索引,可以使用splice()從陣列中刪除它

js
const cities = ["Manchester", "Liverpool", "Edinburgh", "Carlisle"];
const index = cities.indexOf("Liverpool");
if (index !== -1) {
  cities.splice(index, 1);
}
console.log(cities); // [ "Manchester", "Edinburgh", "Carlisle" ]

在此 splice() 呼叫中,第一個引數表示從哪裡開始刪除項,第二個引數表示應該刪除多少個項。因此,您可以刪除多個項

js
const cities = ["Manchester", "Liverpool", "Edinburgh", "Carlisle"];
const index = cities.indexOf("Liverpool");
if (index !== -1) {
  cities.splice(index, 2);
}
console.log(cities); // [ "Manchester", "Carlisle" ]

訪問每個項

通常你會想訪問陣列中的每個項。你可以使用for...of語句來做到這一點

js
const birds = ["Parrot", "Falcon", "Owl"];

for (const bird of birds) {
  console.log(bird);
}

有時您會想對陣列中的每個項執行相同的操作,從而得到一個包含已更改項的陣列。您可以使用map()來做到這一點。下面的程式碼接受一個數字陣列並將每個數字加倍

js
function double(number) {
  return number * 2;
}
const numbers = [5, 2, 7, 6];
const doubled = numbers.map(double);
console.log(doubled); // [ 10, 4, 14, 12 ]

我們將一個函式提供給 map(),並且 map() 會為陣列中的每個項呼叫一次該函式,並傳入該項。然後,它會將每個函式呼叫的返回值新增到新陣列中,最後返回新陣列。

有時您會希望建立一個新陣列,其中只包含原始陣列中符合某些測試的項。您可以使用filter()來做到這一點。下面的程式碼接受一個字串陣列,並返回一個只包含長度大於 8 個字元的字串的陣列

js
function isLong(city) {
  return city.length > 8;
}
const cities = ["London", "Liverpool", "Totnes", "Edinburgh"];
const longer = cities.filter(isLong);
console.log(longer); // [ "Liverpool", "Edinburgh" ]

map() 類似,我們將一個函式提供給 filter() 方法,並且 filter() 會為陣列中的每個項呼叫此函式,並傳入該項。如果該函式返回 true,則該項將新增到新陣列中。最後,它返回新陣列。

字串和陣列之間的轉換

通常你會得到一些包含在很長字串中的原始資料,你可能想把有用的項分離成更有用的形式,然後對它們做一些事情,比如把它們顯示在資料表中。要做到這一點,我們可以使用split()方法。它最簡單的形式是,它接受一個引數,即您想用來分隔字串的字元,並將分隔符之間的子字串作為陣列中的項返回。

注意:好的,這在技術上是一個字串方法,而不是一個數組方法,但我們把它放在陣列部分,因為它在這裡很合適。

  1. 讓我們玩玩這個,看看它是如何工作的。首先,在你的控制檯中建立一個字串

    js
    const data = "Manchester,London,Liverpool,Birmingham,Leeds,Carlisle";
    
  2. 現在讓我們在每個逗號處分割它

    js
    const cities = data.split(",");
    cities;
    
  3. 最後,嘗試查詢新陣列的長度,並從中檢索一些項

    js
    cities.length;
    cities[0]; // the first item in the array
    cities[1]; // the second item in the array
    cities[cities.length - 1]; // the last item in the array
    
  4. 您還可以使用join()方法反向操作。嘗試以下操作

    js
    const commaSeparated = cities.join(",");
    commaSeparated;
    
  5. 將陣列轉換為字串的另一種方法是使用 toString() 方法。toString()join() 更簡單,因為它不需要引數,但限制性更強。使用 join(),您可以指定不同的分隔符,而 toString() 總是使用逗號。(嘗試使用與逗號不同的字元執行步驟 4。)

    js
    const dogNames = ["Rocket", "Flash", "Bella", "Slugger"];
    dogNames.toString(); // Rocket,Flash,Bella,Slugger
    

列印這些產品

輪到你了。在這個練習中,你將回到我們前面描述的例子——在發票上列印產品名稱和價格,然後計算總價並在底部打印出來。按照以下步驟實現該邏輯。

  1. 單擊下面程式碼塊中的**“播放”**以在 MDN Playground 中編輯示例。
  2. // Part 1 註釋下方,有一些字串,每個字串都包含由冒號分隔的產品名稱和價格。我們希望你取消註釋它們並將它們轉換為一個名為 products 的陣列。
  3. // Part 2 註釋下方,開始一個 for...of() 迴圈來遍歷 products 陣列中的每個項。
  4. // Part 3 註釋下方,我們希望你編寫一行程式碼,將當前陣列項 (name:price) 分割成兩個單獨的項,一個包含名稱,一個包含價格。如果你不確定如何做到這一點,請查閱有用的字串方法文章尋求幫助,或者更好地查閱本文的字串和陣列之間的轉換部分。
  5. 作為上述程式碼行的一部分,您還需要將價格從字串轉換為數字。如果您不記得如何做到這一點,請檢視第一篇字串文章
  6. 在程式碼頂部有一個名為 total 的變數被建立並賦值為 0。在迴圈內部(// Part 4 下方),我們希望您新增一行程式碼,在每次迴圈迭代中將當前項價格新增到該總數中,以便在程式碼結束時將正確的總數列印到發票上。您可能需要一個賦值運算子來執行此操作。
  7. 我們希望您更改 // Part 5 之後的下一行,以便 itemText 變數等於“當前商品名稱 — $當前商品價格”,例如“Shoes — $23.99”,以便每個商品都顯示正確的資訊。這是基本的字串連線,如果您一直學習到這裡,應該很熟悉。
  8. 最後,在 // Part 6 註釋下方,您需要新增一個 } 來標記 for...of() 迴圈的結束。

如果你犯了錯誤,可以使用 MDN Playground 中的 _重置_ 按鈕清除你的工作。如果你實在卡住了,可以在即時輸出下方檢視解決方案。

js
const list = document.querySelector(".output ul");
const totalBox = document.querySelector(".output p");
let total = 0;
list.textContent = "";
totalBox.textContent = "";
// Part 1
// "Underpants:6.99",
// "Socks:5.99",
// "T-shirt:14.99",
// "Trousers:31.99",
// "Shoes:23.99",

// Part 2

// Part 3

// Part 4

// Part 5
let itemText = 0;

const listItem = document.createElement("li");
listItem.textContent = itemText;
list.appendChild(listItem);

// Part 6

totalBox.textContent = `Total: $${total.toFixed(2)}`;
點選此處顯示解決方案

你完成的 JavaScript 應該如下所示

js
const list = document.querySelector(".output ul");
const totalBox = document.querySelector(".output p");
let total = 0;
list.textContent = "";
totalBox.textContent = "";

const products = [
  "Underpants:6.99",
  "Socks:5.99",
  "T-shirt:14.99",
  "Trousers:31.99",
  "Shoes:23.99",
];

for (const product of products) {
  const subArray = product.split(":");
  const name = subArray[0];
  const price = Number(subArray[1]);
  total += price;
  const itemText = `${name} — $${price}`;

  const listItem = document.createElement("li");
  listItem.textContent = itemText;
  list.appendChild(listItem);
}

totalBox.textContent = `Total: $${total.toFixed(2)}`;

儲存最近的 5 次搜尋

讓我們完成另一個練習,以保持練習的連續性。

push()pop() 等陣列方法的一個很好的用途是,當您在 Web 應用中維護當前活動項的記錄時。例如,在動畫場景中,您可能有一個表示當前顯示的背景圖形的物件陣列,並且出於效能或避免雜亂的原因,您可能只希望同時顯示 50 個。當新物件建立並新增到陣列中時,可以從陣列中刪除舊物件以保持所需的數量。

在這個例子中,我們將展示一個更簡單的用法——這裡我們給你一個假的搜尋網站,帶有一個搜尋框。我們的想法是,當在搜尋框中輸入詞語時,列表中會顯示前 5 個以前的搜尋詞。當詞語數量超過 5 個時,每次將新詞語新增到頂部時,最舊的詞語就會開始被刪除,因此始終顯示前 5 個詞語。

注意:在真實的搜尋應用中,您可能可以點選之前的搜尋詞以返回到之前的搜尋,並且它會顯示實際的搜尋結果!我們現在只是為了簡單起見。

要完成這個例子,我們需要你

  1. 單擊下面程式碼塊中的**“播放”**以在 MDN Playground 中編輯示例。
  2. // Part 1 註釋下方新增一行程式碼,將當前輸入到搜尋框中的值新增到陣列的開頭。這可以使用 searchInput.value 獲取。
  3. // Part 2 註釋下方新增一行程式碼,移除陣列末尾的當前值。

如果你犯了錯誤,可以使用 MDN Playground 中的 _重置_ 按鈕清除你的工作。如果你實在卡住了,可以在即時輸出下方檢視解決方案。

js
const list = document.querySelector(".output ul");
const searchInput = document.querySelector(".output input");
const searchBtn = document.querySelector(".output button");

list.textContent = "";

const myHistory = [];
const MAX_HISTORY = 5;

searchBtn.addEventListener("click", () => {
  // we will only allow a term to be entered if the search input isn't empty
  if (searchInput.value !== "") {
    // Part 1

    // empty the list so that we don't display duplicate entries
    // the display is regenerated every time a search term is entered.
    list.textContent = "";

    // loop through the array, and display all the search terms in the list
    for (const itemText of myHistory) {
      const listItem = document.createElement("li");
      listItem.textContent = itemText;
      list.appendChild(listItem);
    }

    // If the array length is 5 or more, remove the oldest search term
    if (myHistory.length >= MAX_HISTORY) {
      // Part 2
    }

    // empty the search input and focus it, ready for the next term to be entered
    searchInput.value = "";
    searchInput.focus();
  }
});
點選此處顯示解決方案

你完成的 JavaScript 應該如下所示

js
const list = document.querySelector(".output ul");
const searchInput = document.querySelector(".output input");
const searchBtn = document.querySelector(".output button");

list.textContent = "";

const myHistory = [];
const MAX_HISTORY = 5;

searchBtn.addEventListener("click", () => {
  // we will only allow a term to be entered if the search input isn't empty
  if (searchInput.value !== "") {
    myHistory.unshift(searchInput.value);

    // empty the list so that we don't display duplicate entries
    // the display is regenerated every time a search term is entered.
    list.textContent = "";

    // loop through the array, and display all the search terms in the list
    for (const itemText of myHistory) {
      const listItem = document.createElement("li");
      listItem.textContent = itemText;
      list.appendChild(listItem);
    }

    // If the array length is 5 or more, remove the oldest search term
    if (myHistory.length >= MAX_HISTORY) {
      myHistory.pop();
    }

    // empty the search input and focus it, ready for the next term to be entered
    searchInput.value = "";
    searchInput.focus();
  }
});

總結

閱讀完本文後,我們相信您會同意陣列似乎非常有用;您會在 JavaScript 中隨處看到它們,通常與迴圈結合使用,以便對陣列中的每個項執行相同的操作。我們將在模組後面的部分中向您介紹有關迴圈的所有知識。

在下一篇文章中,我們將為您提供一些測試,您可以使用它們來檢查您對我們提供的陣列資訊的理解和記憶程度。

另見

Array

Array 物件參考頁面提供了本頁討論的功能以及許多其他 Array 功能的詳細參考指南。