現代 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 或一般前端程式時,可以優先用這些新方法,讓程式碼更乾淨、語意更明確,也比較不會踩到原陣列被偷偷修改的坑。