JavaScript 陣列操作:includes、indexOf 與 lastIndexOf
簡介
在日常開發中,陣列是最常見的資料結構之一。無論是處理使用者輸入、API 回傳資料,還是實作演算法,都離不開對陣列的搜尋與判斷。JavaScript 為我們提供了三個非常實用的原生方法:includes、indexOf 以及 lastIndexOf。它們分別用於判斷元素是否存在、取得第一個匹配元素的索引,以及取得最後一個匹配元素的索引。掌握這三個方法,能讓你在寫程式時少寫迴圈、減少錯誤,並提升程式的可讀性與效能。
本篇文章將以淺顯易懂的語言,從概念說明、實作範例、常見陷阱,到最佳實踐與真實應用場景,完整介紹 includes、indexOf 與 lastIndexOf 的使用方式。即使你是剛踏入 JavaScript 的新手,也能快速上手;若你已具備一定基礎,亦能從中發掘更進階的技巧。
核心概念
1. Array.prototype.includes(value, fromIndex?)
includes 會檢查陣列中是否 至少出現一次 指定的 value,回傳布林值 true 或 false。第二個參數 fromIndex(可選)表示搜尋起始位置,支援負數表示從陣列尾端往前算。
特點
- 直接回傳 布林,不需要再比較索引。
- 使用 嚴格相等(===) 進行比較,
NaN也是例外(includes能正確偵測NaN)。 - 讀寫簡潔,適合 是否存在 的判斷。
範例 1:基本用法
const fruits = ['apple', 'banana', 'orange'];
console.log(fruits.includes('banana')); // true
console.log(fruits.includes('grape')); // false
範例 2:從指定位置開始搜尋
const numbers = [1, 2, 3, 2, 1];
console.log(numbers.includes(2, 2)); // true // 從索引 2 開始,找到最後一個 2
console.log(numbers.includes(1, -2)); // false // 從倒數第二個元素往前找,找不到 1
範例 3:偵測 NaN
const mixed = [0, NaN, undefined];
console.log(mixed.includes(NaN)); // true
2. Array.prototype.indexOf(searchElement, fromIndex?)
indexOf 會回傳 第一個 出現 searchElement 的索引,若找不到則回傳 -1。同樣支援 fromIndex,負數會從陣列尾端計算起始位置。
特點
- 回傳 索引,適合需要知道元素位置的情況。
- 使用 嚴格相等(===),但 無法偵測
NaN(因為NaN !== NaN)。 - 在字串或類似陣列的物件(如
arguments)上也可使用。
範例 4:基本搜尋
const colors = ['red', 'green', 'blue', 'green'];
console.log(colors.indexOf('green')); // 1 (第一個 green 的位置)
console.log(colors.indexOf('yellow')); // -1
範例 5:指定起始索引
const letters = ['a', 'b', 'c', 'b', 'a'];
console.log(letters.indexOf('b', 2)); // 3 // 從索引 2 開始找,回傳後面的 b
範例 6:NaN 無法偵測
const arr = [NaN, 1, 2];
console.log(arr.indexOf(NaN)); // -1 // 因為 NaN !== NaN
3. Array.prototype.lastIndexOf(searchElement, fromIndex?)
lastIndexOf 與 indexOf 相似,但它會 從右至左(即從陣列尾端)搜尋,回傳最後一次出現的索引;若找不到則回傳 -1。同樣支援 fromIndex,負數會從尾端往左算起。
特點
- 用於 取得最後一次出現 的位置。
- 同樣使用 嚴格相等,無法偵測
NaN。 - 常見於需要去除重複元素、或找出最近的匹配項目。
範例 7:基本用法
const nums = [5, 10, 15, 10, 20];
console.log(nums.lastIndexOf(10)); // 3 // 最後一個 10 的索引
console.log(nums.lastIndexOf(30)); // -1
範例 8:搭配 fromIndex
const data = [1, 2, 3, 2, 1];
console.log(data.lastIndexOf(2, 2)); // 1 // 只搜尋到索引 2 為止
範例 9:NaN 無法偵測
const weird = [NaN, 0, NaN];
console.log(weird.lastIndexOf(NaN)); // -1
常見陷阱與最佳實踐
| 陷阱 | 說明 | 建議的解決方式 |
|---|---|---|
indexOf、lastIndexOf 無法偵測 NaN |
NaN !== NaN,因此這兩個方法找不到 NaN。 |
若需要檢測 NaN,改用 includes 或自行使用 Array.prototype.some。 |
忽略 fromIndex 的負數行為 |
負數會從陣列尾端算起,若不熟悉會產生錯誤結果。 | 在使用前先檢查 fromIndex 的意圖,或使用 Array.prototype.slice 產生子陣列再搜尋。 |
使用 indexOf 判斷存在性 |
直接比較 !== -1 雖可行,但可讀性較差。 |
建議使用 includes,語意更清晰。 |
| 搜尋物件或陣列時的相等性 | 只能比較同一個參考,不同但內容相同的物件不會匹配。 | 若要深度比較,需自行實作或使用 lodash.isEqual 等工具函式。 |
| 大陣列的效能 | includes、indexOf、lastIndexOf 都是 線性搜尋,最壞情況 O(n)。 |
若搜尋頻繁且陣列很大,考慮使用 Set、Map 或建立索引結構。 |
最佳實踐
- 語意優先:判斷「是否存在」時,優先使用
includes;需要索引時才使用indexOf/lastIndexOf。 - 避免硬編碼
-1:可寫成if (arr.includes(item)),或封裝成function contains(arr, val){ return arr.includes(val); },提升可讀性。 - 結合
filter、map:在需要同時取得位置與值時,可先使用map產生索引陣列,再配合filter篩選。 - 利用
Set取代頻繁搜尋:const set = new Set(arr); set.has(value);在大量搜尋時效能更佳。
實際應用場景
1. 表單驗證 – 防止重複提交
在使用者點擊「送出」按鈕時,常需要檢查已提交的項目是否已存在於暫存陣列中:
let submittedIds = [101, 203, 304];
function canSubmit(id) {
// 使用 includes 判斷是否已提交
return !submittedIds.includes(id);
}
// 範例
if (canSubmit(203)) {
// 送出
} else {
console.warn('此項目已提交過!');
}
2. 文字搜尋 – 高亮顯示關鍵字
在一段文字中找出所有關鍵字的出現位置,最後一次出現的位置常用於決定捲動位置:
const paragraph = 'JavaScript 是一門靈活的程式語言,JavaScript 也被廣泛應用於前端。';
const keyword = 'JavaScript';
const firstIdx = paragraph.indexOf(keyword);
const lastIdx = paragraph.lastIndexOf(keyword);
console.log(`第一個出現在 ${firstIdx},最後一個出現在 ${lastIdx}`);
3. 去除陣列重複元素(保留最後一次出現)
有時候需要保留每個值最後一次出現的順序,可結合 lastIndexOf:
function uniqKeepLast(arr) {
return arr.filter((item, idx) => arr.lastIndexOf(item) === idx);
}
const data = [1, 2, 3, 2, 4, 1];
console.log(uniqKeepLast(data)); // [3, 2, 4, 1]
4. 互動式選單 – 高亮最近點選的項目
在多選清單中,使用者點選過的項目需要被「標記」:
let history = []; // 紀錄點選順序
function select(item) {
if (!history.includes(item)) {
history.push(item);
}
// 高亮最近一次點選的項目
const lastIdx = history.lastIndexOf(item);
console.log(`項目 ${item} 最近一次出現在索引 ${lastIdx}`);
}
總結
includes、indexOf、lastIndexOf 是 JavaScript 陣列搜尋 的三大基礎工具。
includes以 布林 回傳是否存在,語意最清晰,且能正確偵測NaN。indexOf取得 第一個匹配 的索引,適合需要定位元素的位置。lastIndexOf取得 最後一個匹配 的索引,常用於去重或找出最近的出現點。
在實務開發中,依照需求選擇最合適的方法,並留意 相等性、NaN、fromIndex 等細節,可避免常見錯誤、提升程式可讀性與效能。結合 Set、Map 或自訂函式,甚至利用這三個方法的特性實作更複雜的演算法,都是提升 JavaScript 技能的好方法。希望本篇文章能幫助你在陣列操作上更加得心應手,寫出更乾淨、可靠的程式碼!