初學者指南:深入了解 JavaScript reduce() 方法

更新日期: 2025 年 1 月 23 日

在 JavaScript 中,陣列方法提供了許多強大的工具來處理資料,reduce() 是其中最常用也最靈活的方法之一。

reduce() 允許你將陣列中的每個元素累積到一個單一的結果中,不論這個結果是數字、字串、物件,還是其他類型的資料。

這篇文章將為新手介紹 reduce() 方法,並通過簡單範例解釋它的工作原理和應用場景。


什麼是 reduce()

reduce() 方法會對陣列中的每個元素執行回調函數,最終將結果累積成單一的值並返回。

這個方法常用於將陣列壓縮成一個值,例如計算陣列元素的總和、乘積,甚至可以用來處理物件。

語法

array.reduce(function(accumulator, currentValue, currentIndex, array) {
  // 回調函數邏輯
}, initialValue)
  • accumulator:累積器,保存每次回調函數執行後的累積結果。
  • currentValue:當前正在處理的陣列元素。
  • currentIndex(可選):當前元素的索引。
  • array(可選):被操作的陣列。
  • initialValue(可選):累積器的初始值。如果不提供,默認使用陣列的第一個元素作為初始值,並從第二個元素開始迭代。

reduce() 的基本應用

計算陣列總和

讓我們從一個簡單的範例開始,計算一個數字陣列的總和。

範例:

let numbers = [1, 2, 3, 4];
let sum = numbers.reduce(function(accumulator, currentValue) {
  return accumulator + currentValue;
}, 0);

console.log(sum); // 輸出:10

說明:

  • accumulator 開始時為初始值 0,然後依次將每個元素累加到累積器中。
  • 最終,reduce() 返回所有數字的總和。

計算陣列的乘積

除了總和,你還可以使用 reduce() 來計算乘積。

範例:

let numbers = [1, 2, 3, 4];
let product = numbers.reduce(function(accumulator, currentValue) {
  return accumulator * currentValue;
}, 1);

console.log(product); // 輸出:24

說明:

  • 在這裡,我們使用初始值 1,然後每次將累積器與當前值相乘,最後得到陣列中所有數字的乘積。

進階應用

陣列中找出最大值

reduce() 也可以用來查找陣列中的最大值。

範例:

let numbers = [10, 20, 30, 40, 50];
let max = numbers.reduce(function(accumulator, currentValue) {
  return Math.max(accumulator, currentValue);
});

console.log(max); // 輸出:50

說明:

  • 每次迭代時,Math.max() 用來比較 accumulatorcurrentValue,並返回較大的值。最終結果就是陣列中的最大值。

將陣列轉換為物件

reduce() 可以將一個陣列轉換成物件,這在處理複雜資料時非常實用。

範例:

let people = [
  { name: 'Alice', age: 25 },
  { name: 'Bob', age: 30 },
  { name: 'Charlie', age: 35 }
];

let peopleByName = people.reduce(function(accumulator, person) {
  accumulator[person.name] = person.age;
  return accumulator;
}, {});

console.log(peopleByName);
// 輸出:{ Alice: 25, Bob: 30, Charlie: 35 }

逐步解釋

  1. people 陣列:首先,people 是一個陣列,其中每個元素都是一個物件,包含 nameage 的資訊。範例如下:
   let people = [
     { name: 'Alice', age: 25 },
     { name: 'Bob', age: 30 },
     { name: 'Charlie', age: 35 }
   ];
  1. reduce() 方法reduce() 會遍歷整個 people 陣列,並將每個元素(人員物件)依次傳入回調函數中,最終累積結果(即一個物件)會被返回。
  2. accumulator
    • accumulator 是回調函數的第一個參數,它是累積結果的容器。在每次迭代中,這個累積結果會被更新。
    • 在這段代碼中,accumulator 初始化為一個空物件 {},這是在 reduce() 方法的第二個參數中指定的。
    • accumulator 在每次迭代中會累積一個屬性,該屬性是當前遍歷的人員的 name,對應的值是該人員的 age
  3. person
    • personpeople 陣列中的當前元素,也就是當前的每個人員物件。person 會依次從陣列中取出,並被傳入回調函數。
  4. 迭代過程
    • 在每次迭代中,personname 屬性被用作 accumulator 物件的鍵,而 age 屬性則被用作該鍵對應的值。
  5. 代碼詳解
    • accumulator[person.name] = person.age;:這行代碼動態地為 accumulator 物件新增屬性,person.name 作為屬性名,person.age 作為該屬性的值。
    • 然後 return accumulator;:將更新過的 accumulator 返回,這樣它能夠在下一次迭代中繼續累積新的值。
  6. 最終結果
    • 當所有的陣列元素都被處理後,accumulator(即 peopleByName) 將是一個物件,其中每個人的名字是物件的屬性,對應的值是年齡。

那麼,這段代碼最終會生成以下物件:

let peopleByName = {
  Alice: 25,
  Bob: 30,
  Charlie: 35
};

計算陣列中元素的出現次數

你也可以使用 reduce() 來計算陣列中每個元素出現的次數。

範例:

let fruits = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple'];
let fruitCount = fruits.reduce(function(accumulator, fruit) {
  if (accumulator[fruit]) {
    accumulator[fruit]++;
  } else {
    accumulator[fruit] = 1;
  }
  return accumulator;
}, {});

console.log(fruitCount);
// 輸出:{ apple: 3, banana: 2, orange: 1 }

說明:

這段代碼的目的是計算 fruits 陣列中每種水果出現的次數,並將結果存放在一個物件中,最終輸出如下:

{ apple: 3, banana: 2, orange: 1 }

代碼逐步解釋

  1. fruits 陣列:這是一個包含多個水果名稱的陣列,其中一些水果是重複的。

    我們的目標是計算每種水果在陣列中出現的次數。
   let fruits = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple'];
  1. reduce() 方法reduce() 是一個強大的陣列方法,它用於累積陣列中的值,並生成一個最終結果。

    這裡,我們將使用 reduce() 將水果出現的次數累積到一個物件中。 語法如下:
   array.reduce(function(accumulator, currentValue) {
     // 操作邏輯
   }, initialValue);
  • accumulator:累積器,用來保存計數結果,初始值是 {}(一個空物件)。
  • fruit:當前正在遍歷的水果元素,即陣列中的每一個水果。
  • initialValue:在這裡我們指定為一個空物件 {},它用來存放每種水果的出現次數。
  1. 累積器的作用accumulator 是一個物件,用來保存每種水果的名稱作為鍵,並將該水果出現的次數存儲為鍵值對的值。
  1. 回調函數邏輯:每次迭代時,我們會檢查當前水果(fruit)是否已經存在於 accumulator 物件中。

    如果存在,則將其對應的計數值加 1。如果不存在,則將該水果新增到 accumulator 中,並將其計數設為 1。

    詳解:
    • if (accumulator[fruit])
      檢查 accumulator 中是否已經有當前的 fruit。如果有(即鍵已經存在),那麼就進行加 1 操作,表示該水果已經出現過。
    • accumulator[fruit]++
      如果該水果已經在 accumulator 中,則將其對應的計數值加 1。
    • else
      如果該水果尚未出現過,則進入 else,在 accumulator 中新增該水果作為鍵,並將其值設為 1,表示該水果第一次出現。
  1. 返回累積器:每次回調函數執行完畢時,reduce() 會返回更新過的 accumulator,並將其傳遞給下一次迭代。
  2. 最終結果:當所有的水果都被遍歷完後,reduce() 的最終結果會是一個物件,這個物件包含了每種水果的出現次數。

範例執行流程:

fruits 陣列為例,我們逐步跟蹤 reduce() 如何處理這個陣列。

第一次迭代:

  • fruit = 'apple'
  • accumulator = {}(初始化為空物件)
  • 'apple' 不在 accumulator 中,於是 accumulator['apple'] = 1

此時 accumulator 變成:

{ apple: 1 }

第二次迭代:

  • fruit = 'banana'
  • 'banana' 不在 accumulator 中,於是 accumulator['banana'] = 1

此時 accumulator 變成:

{ apple: 1, banana: 1 }

第三次迭代:

  • fruit = 'apple'
  • 'apple' 已經存在於 accumulator 中,於是 accumulator['apple']++,即計數加 1。

此時 accumulator 變成:

{ apple: 2, banana: 1 }

第四次迭代:

  • fruit = 'orange'
  • 'orange' 不在 accumulator 中,於是 accumulator['orange'] = 1

此時 accumulator 變成:

{ apple: 2, banana: 1, orange: 1 }

第五次迭代:

  • fruit = 'banana'
  • 'banana' 已經存在於 accumulator 中,於是 accumulator['banana']++,即計數加 1。

此時 accumulator 變成:

{ apple: 2, banana: 2, orange: 1 }

第六次迭代:

  • fruit = 'apple'
  • 'apple' 已經存在於 accumulator 中,於是 accumulator['apple']++,即計數加 1。

此時最終的 accumulator 變成:

{ apple: 3, banana: 2, orange: 1 }

最終結果

當所有元素都被處理完後,fruitCount 會得到以下結果:

{ apple: 3, banana: 2, orange: 1 }

這個物件表明 apple 出現了 3 次,banana 出現了 2 次,而 orange 出現了 1 次。

在進階應用部分,可以新增以下區塊來解釋該代碼的應用原理:

reduce() 代碼輸出特定結果

這段代碼用 reduce() 遍歷數組,但每次迭代的回調函數都返回 1,而不是使用累積結果,因此輸出的是單一的值 1,而非基於累積計算的結果。

const number = [1, 3, 4, 7, 8, 9];
number.reduce((acc, cv) => {
  return 1;
}, 0);

每次迭代回傳 1,累積值被忽略,最終結果為 1

在這段 reduce() 代碼中,累積器 acc 的值會隨著每次迭代的返回值進行更新,因此理解每次迭代的過程對理解 reduce() 很重要。

第一輪迭代

  • 初始值acc 的初始值是 0,這是通過 reduce 的第二個參數設置的。
  • cv(當前值)cv 是數組的第一個元素 1
  • 返回值:這一輪的回調函數僅返回固定的 1,因此下一輪的 acc 將是 1

第二輪迭代

  • acc:這一輪的 acc 是上一輪返回的值,即 1
  • cv(當前值):這次的 cv 是數組的第二個元素 3
  • 返回值:再次,回調函數返回 1,這使得 acc 仍然保持為 1,而不受 cv 值的影響。

第三輪迭代

  • acc:此時的 acc 繼續保持 1
  • cv(當前值):現在的 cv 是數組的第三個元素 4
  • 返回值:回調函數依然返回固定的 1,因此 acc 依舊維持不變。

第四輪迭代

  • acc:此輪的 acc 還是 1,因為每一輪的返回值都固定為 1
  • cv(當前值):這次的 cv 是數組的第四個元素 7
  • 返回值:回調函數繼續返回 1,導致 acc 保持為 1

第五輪迭代

  • accacc 再次保持為 1
  • cv(當前值):這次的 cv 是數組的第五個元素 8
  • 返回值:回調函數返回 1,使 acc 繼續保持 1

第六輪迭代

  • acc:累積器 acc 仍然是 1
  • cv(當前值):最後一個 cv 是數組的第六個元素 9
  • 返回值:回調函數再次返回 1,結束所有迭代。

整個過程中,無論 cv 的值如何變化,回調函數總是返回固定的 1,所以每一輪的 acc 都被更新為 1,並且保持不變。

最終,reduce() 的結果也就是 1,而不是基於數組元素的任何計算結果。


提供 initialValue 的重要性

在使用 reduce() 時,提供初始值 initialValue 通常是很重要的。

這可以確保累積器有一個確定的初始狀態,並且能夠在處理空陣列,或非數值類型的資料時避免錯誤。

初始值對空陣列的影響

範例:

let numbers = [];
let sum = numbers.reduce(function(accumulator, currentValue) {
  return accumulator + currentValue;
}, 0);

console.log(sum); // 輸出:0

如果我們沒有提供初始值,reduce() 會嘗試將陣列中的第一個元素作為初始值,這在處理空陣列時會導致錯誤。

提供 initialValue 能夠確保即使陣列為空,累積器仍然能夠正常工作。


reduce() vs 其他陣列方法

你可能會發現,reduce() 可以用來實現許多其他陣列方法的功能,例如 map()filter()find()

事實上,reduce() 是一個非常靈活和強大的工具,它可以根據需要進行高度自定義。

例如,用 reduce() 來實現一個簡單的 map() 功能:

let numbers = [1, 2, 3, 4];
let squared = numbers.reduce(function(accumulator, currentValue) {
  accumulator.push(currentValue * currentValue);
  return accumulator;
}, []);

console.log(squared); // 輸出:[1, 4, 9, 16]

這個範例顯示了如何使用 reduce() 將每個數字平方並存儲在一個新的陣列中。

雖然 map() 專門用於這種操作,但 reduce() 也能夠達到同樣的效果,並且提供了更大的靈活性。


結語

JavaScript 的 reduce() 方法是一個強大且靈活的工具,能夠對陣列進行各種操作,從簡單的總和計算到更複雜的資料結構轉換。

理解 reduce() 的工作原理,將有助於你在開發中有效處理陣列資料,並且能夠將多步操作濃縮到一個清晰的流程中。

隨著你對 reduce() 的理解加深,你會發現它是 JavaScript 中不可或缺的一部分,無論是處理數值運算還是進行更複雜的資料轉換,reduce() 都能大大簡化你的程式碼。


參考資料

Similar Posts