JavaScript 陣列操作:slice 與 splice 完全指南
簡介
在 JavaScript 中,陣列(Array)是最常使用的資料結構之一,幾乎每個前端或 Node.js 專案都離不開它。掌握陣列的切割與修改技巧,不僅能讓程式碼更簡潔,也能提升效能與可讀性。
Array.prototype.slice 與 Array.prototype.splice 是兩個外觀相似、卻功能截然不同的 API。slice 用於 非破壞性(non‑mutating)的取子陣列,而 splice 則是 破壞性(mutating)的增刪改工具。對於新手而言,常因兩者的參數與回傳值混淆而踩到坑;對於中階開發者,則需要了解它們在大型資料處理、immutable 程式設計等情境下的最佳使用方式。
本篇文章將從概念說明、實作範例、常見陷阱到實務應用,完整闡述 slice 與 splice,幫助你在日常開發中得心應手。
核心概念
1. slice:取出子陣列(不改變原始陣列)
| 參數 | 說明 |
|---|---|
begin (可選) |
起始索引(包含),支援負值,負值表示從陣列尾端倒數。預設值為 0。 |
end (可選) |
結束索引(不包含),同樣支援負值。若省略,則切到陣列最後。 |
回傳值 為 新陣列,原陣列保持不變。
範例 1:基本用法
const fruits = ['🍎', '🍊', '🍇', '🍉', '🥭'];
// 取出第 1~3 個元素(含第 1,不含第 3) -> ['🍊', '🍇']
const part = fruits.slice(1, 3);
console.log(part); // ['🍊', '🍇']
console.log(fruits); // 原陣列未變
範例 2:使用負索引
// 從倒數第 2 個開始取到最後 -> ['🥭']
const lastTwo = fruits.slice(-2);
console.log(lastTwo); // ['🍉', '🥭']
範例 3:淺拷貝(shallow copy)
// 直接不傳參數,可得到原陣列的淺拷貝
const copy = fruits.slice();
copy[0] = '🍌';
console.log(copy); // ['🍌', '🍊', '🍇', '🍉', '🥭']
console.log(fruits); // 仍是原本的 ['🍎', '🍊', '🍇', '🍉', '🥭']
註:
slice只做「淺層」拷貝,若陣列裡的元素本身是物件或其他陣列,拷貝後仍指向同一個參考。
2. splice:在原位增刪改(會改變原始陣列)
| 參數 | 說明 |
|---|---|
start |
起始索引(必填),負值同樣表示從尾端倒數。 |
deleteCount (可選) |
要刪除的元素數量,若為 0 表示不刪除。若省略,則刪除從 start 起到陣列結尾的所有元素。 |
item1, item2, ... (可選) |
要插入的新元素,可傳入任意數量。 |
回傳值 為 被刪除的元素組成的陣列(若未刪除則回傳空陣列),而原陣列則被 直接修改。
範例 4:刪除元素
let colors = ['red', 'green', 'blue', 'yellow'];
// 從索引 1 開始,刪除 2 個元素 -> ['green', 'blue']
const removed = colors.splice(1, 2);
console.log(removed); // ['green', 'blue']
console.log(colors); // ['red', 'yellow']
範例 5:插入元素
// 在索引 2 的位置插入兩個元素,且不刪除任何東西
colors.splice(2, 0, 'orange', 'purple');
console.log(colors); // ['red', 'yellow', 'orange', 'purple']
範例 6:同時刪除與插入(取代)
// 將索引 1 的元素取代成 'cyan' 與 'magenta'
colors.splice(1, 1, 'cyan', 'magenta');
console.log(colors); // ['red', 'cyan', 'magenta', 'orange', 'purple']
範例 7:刪除至結尾
// 從索引 3 開始,刪除到結尾
const tail = colors.splice(3);
console.log(tail); // ['orange', 'purple']
console.log(colors); // ['red', 'cyan', 'magenta']
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方式 |
|---|---|---|
誤以為 slice 會改變原陣列 |
初學者常把 slice 當成「切掉」的動作,結果發現原陣列沒變。 |
記住 「slice = 取子,不改變」;若真的要改變,改用 splice 或 Array.prototype.copyWithin。 |
splice 的返回值被忽略 |
splice 回傳被刪除的項目,若忘記接收,會失去重要資訊。 |
將返回值存起來(如 const removed = arr.splice(...);),或直接在需要時使用。 |
| 負索引的行為差異 | slice(-1) 取最後一個元素,splice(-1, 1) 則會 刪除 最後一個元素。 |
先在小範例測試負索引行為,避免意外刪除。 |
| 一次刪除太多導致資料遺失 | splice(start) 若忘記 deleteCount,會一次刪除至結尾。 |
明確寫出 deleteCount,或使用 slice 先取子陣列再自行處理。 |
| 淺層拷貝的副作用 | slice、splice 都是淺層操作,對於嵌套物件仍是同一個參考。 |
若需要深層拷貝,可使用 JSON.parse(JSON.stringify(arr)) 或 structuredClone(Node 17+)。 |
最佳實踐
盡量保持資料不可變(immutable)
在 React、Vue 等框架中,slice常用於產生新陣列以觸發 UI 更新;避免直接使用splice,除非確定不會破壞 state。使用解構賦值搭配
sliceconst [first, ...rest] = array.slice(1);這樣寫更具可讀性,且不會改變原陣列。
一次性完成多步驟操作
若需要「刪除 + 插入」的取代功能,直接使用splice而非先slice再push,可減少遍歷次數,提高效能。避免在大型陣列上頻繁
splicesplice會造成後續元素的搬移,時間複雜度為 O(n)。對於大規模資料,考慮 Linked List 或 Map 結構,或使用 immutable.js 等專門的資料結構庫。
實際應用場景
1. 分頁資料顯示
在前端分頁時,常需要根據目前頁碼取出對應的子集合:
function paginate(arr, page = 1, pageSize = 10) {
const start = (page - 1) * pageSize;
return arr.slice(start, start + pageSize);
}
// 範例
const data = Array.from({ length: 95 }, (_, i) => i + 1); // [1,2,...,95]
console.log(paginate(data, 3, 20)); // 第 3 頁,每頁 20 筆 -> [41~60]
2. 從清單中移除已完成的任務
在待辦清單(Todo List)應用中,點擊「完成」按鈕時,需要把該項目從陣列中移除:
function removeTask(tasks, id) {
const index = tasks.findIndex(t => t.id === id);
if (index !== -1) {
// 直接修改原陣列(若使用 Redux,應改為不變性寫法)
tasks.splice(index, 1);
}
return tasks;
}
// 使用
let todos = [{id:1, txt:'買牛奶'}, {id:2, txt:'寫程式'}];
removeTask(todos, 1);
console.log(todos); // [{id:2, txt:'寫程式'}]
3. 文字編輯器的「插入」功能
在簡易的文字編輯器裡,我們可能要在字串陣列的特定位置插入新字元:
function insertChars(arr, pos, ...chars) {
arr.splice(pos, 0, ...chars);
return arr;
}
let chars = ['H', 'e', 'l', 'o'];
insertChars(chars, 3, 'l'); // 在索引 3 前插入 'l'
console.log(chars.join('')); // "Hello"
4. 產生「前/後」子集合(Immutable 風格)
在 React 中,我們常會需要保留舊的陣列,同時產生新陣列:
// 取得前 3 個元素,並在最後插入新項目
const newList = [...oldList.slice(0, 3), newItem];
總結
slice不會改變原陣列,適合取子集合、製作淺拷貝或實作 immutable 流程。splice會直接修改原陣列,是一次完成 刪除、插入、取代 的利器,但使用時要留意副作用。- 了解兩者的參數規則(特別是負索引)與回傳值,可避免常見的「資料遺失」或「意外變更」問題。
- 在實務開發中,依需求選擇正確的 API:UI 更新 多用
slice,資料庫同步、一次性變更 則可考慮splice。 - 最後,保持 可讀性、效能 與 資料不可變 的原則,才能寫出既簡潔又可靠的程式碼。
祝你在 JavaScript 陣列操作的道路上,玩得開心、寫得順手! 🎉