Skip to main content

現代 JavaScript 陣列新方法速查

一次整理幾個實用的「現代 JS 陣列方法」,包含 .at().findLast().flat().flatMap().toSorted().toReversed(),並搭配「舊寫法 vs 新寫法」對照,方便實務開發時快速套用。

為什麼要關心這些新方法?

  • 可讀性更高:很多以前需要「反轉、展開、手動計算 index」的寫法,現在都可以用一行語意清楚的方法取代。
  • 避免不小心改到原陣列:像 .toSorted().toReversed() 都會回傳新陣列,不會動到原本的資料,對函數式程式風格特別友善。
  • 瀏覽器支援度已經相當不錯:在現代瀏覽器與 Node.js 版本中基本都能直接使用,適合作為新的預設寫法。

1. .at():乾淨又直覺的索引存取(支援負索引)

.at() 可以用來依照陣列位置取得元素,支援負數索引,從尾巴開始數。

舊寫法:

const arr = [10, 20, 30, 40];

// 拿最後一個元素
const last = arr[arr.length - 1];

新寫法:

const arr = [10, 20, 30, 40];

const last = arr.at(-1); // 40
const first = arr.at(0); // 10

幾個重點:

  • arr.at(-1) 表示「從最後一個開始往前數第 1 個」。
  • arr[-1] 不同,後者在 JavaScript 中會是 undefined,因為陣列索引不是這樣設計的。
  • .at() 也可以用在字串上,例如:'hello'.at(-1) 會得到 'o'

2. .findLast():從尾端開始找最後一個符合條件的元素

.findLast()從陣列尾端往前找,回傳最後一個符合條件的元素。

舊寫法(常見但有點繞):

const nums = [2, 4, 7, 4, 9];

// 先反轉再 find
const lastEven = nums.slice().reverse().find((n) => n % 2 === 0);

新寫法:

const nums = [2, 4, 7, 4, 9];

const lastEven = nums.findLast((n) => n % 2 === 0); // 4

優點:

  • 不需要 reverse() 或建立額外陣列。
  • 直接表達 intention:「從後面找最後一個符合條件的元素」。

3. .findLastIndex():需要的是索引,而不是值的時候

.findLastIndex().findLast() 類似,但回傳的是索引 index

舊寫法(需要反轉 + 再算回原本的 index):

const nums = [1, 3, 7, 3, 9];

const reversedIndex = nums
.slice()
.reverse()
.findIndex((n) => n === 3);

const lastIndex = nums.length - 1 - reversedIndex;
// lastIndex === 3

新寫法:

const nums = [1, 3, 7, 3, 9];

const lastIndex = nums.findLastIndex((n) => n === 3); // 3

這樣就不需要做「長度 - 1 - index」這種容易看了頭痛的運算。

4. .flat():把巢狀陣列展平成一層

.flat() 用來「攤平」巢狀陣列,預設會攤平一層,也可以指定要往內幾層。

舊寫法(用 reduce + concat 自己組):

const nested = [1, [2, [3, 4]]];

const flat = nested.reduce((acc, val) => acc.concat(val), []);
// [1, 2, [3, 4]]

新寫法:

const nested = [1, [2, [3, 4]]];

const flat1 = nested.flat(); // [1, 2, [3, 4]] 預設深度 1
const flat2 = nested.flat(2); // [1, 2, 3, 4] 攤平 2 層
const flatAll = nested.flat(Infinity); // 完全攤平到沒有巢狀

注意:

  • .flat() 會回傳新陣列,不會改變原來的 nested
  • 如果資料來源是 API 回傳、常常巢狀很深,flat(Infinity) 在整理資料時非常方便。

5. .flatMap():先 map 再 flat,一次完成

.flatMap() 等於先 map()flat(1),會先把每個元素映射成一個陣列,然後攤平成一層。

舊寫法:

const arr = [1, 2, 3];

const doubled = arr.map((x) => [x, x * 2]).flat();
// [1, 2, 2, 4, 3, 6]

新寫法:

const arr = [1, 2, 3];

const doubled = arr.flatMap((x) => [x, x * 2]);
// [1, 2, 2, 4, 3, 6]

使用情境:

  • 把一個 item 展開成「多個結果」的時候很實用,例如:
    • 把句子切成單字列表。
    • 把每個 user 展開成多筆 event。

6. .toSorted():取得排序後的新陣列,不改原本資料

.sort()改變原陣列本身,在專案稍微複雜一點時,非常容易造成找不到的 side effect。

傳統寫法(需要先複製再 sort):

const arr = [3, 1, 2];

// 為了避免改到原陣列,先展開複製一份
const sorted = [...arr].sort((a, b) => a - b);

新寫法:

const arr = [3, 1, 2];

const sorted = arr.toSorted((a, b) => a - b);

console.log(sorted); // [1, 2, 3]
console.log(arr); // [3, 1, 2] 原本的陣列不變

優點:

  • 語意清楚:一看就知道這是「產生排序後的新陣列」。
  • 不會誤改原陣列,適合搭配 React/Vue 這類需要 immutable 資料結構思維的框架。

7. .toReversed():反轉但不修改原陣列

.toSorted() 類似,.toReversed() 是非破壞性版本的 reverse()

舊寫法:

const arr = [1, 2, 3];

const reversed = [...arr].reverse();
// reversed: [3, 2, 1]
// arr: [1, 2, 3]

新寫法:

const arr = [1, 2, 3];

const reversed = arr.toReversed();

console.log(reversed); // [3, 2, 1]
console.log(arr); // [1, 2, 3]

搭配 .toSorted() 一樣,不會有隱藏的資料被改掉的問題。

8. 小小組合範例:取得最後一個符合條件的使用者

以下是一個把多個方法串起來的小例子:從 user 陣列中,抓出「最後一個 active 的使用者名稱」。

const users = [
{ name: 'Alice', active: false },
{ name: 'Bob', active: true },
{ name: 'Carol', active: true },
];

const lastActiveName = users
.filter((u) => u.active) // 只留下 active 的
.map((u) => u.name) // 只保留名字
.toReversed() // 反轉,但不改原陣列
.at(0); // 取第一個,也就是原本最後一個 active 的

console.log(lastActiveName); // 'Carol'

在更現代的環境裡,你也可以這樣寫,直接用 .findLast()

const lastActiveUser = users.findLast((u) => u.active);
const lastActiveName = lastActiveUser?.name ?? null;

總結:可以開始改掉的舊寫法

如果你平常在專案裡還常常看到下面這些 pattern,可以考慮慢慢改成新方法:

  • arr[arr.length - 1] 取最後一筆資料 → 換成 .at(-1)
  • 為了找最後一個符合條件的元素,把陣列 reverse()find → 換成 .findLast() / .findLastIndex()
  • 自己用 reduce + concat 攤平陣列 → 換成 .flat() / .flatMap()
  • 為了保持 immutability,先 [...].sort() / .reverse() → 換成 .toSorted() / .toReversed()

之後在寫 Vue / React 或一般前端程式時,可以優先用這些新方法,讓程式碼更乾淨、語意更明確,也比較不會踩到原陣列被偷偷修改的坑。