我是阿傑,由於 splice() 這個咩色的 ECMAScript 的演算法非常複雜,為了避免文章過於龐大,會將其拆成 2 天介紹:
第 1 天 (Day 26):
第 2 天 (Day 27)
splice() 這個 method 的全寫應該是 Array.prototype.splice,有興趣可以看 Day 2 的介紹,這邊會直接使用 splice() 作為替代。
範例的 callback 都會使用箭頭函式做介紹,如果尚不熟悉的話可以參考 MDN 的介紹。
最後會透過分析 ECMAScript 來驗證是否有吻合,如果覺得 ECMAScript 有點艱澀難懂,我們在 Day 4 、Day 5 有介紹其相關術語可以幫助閱讀。
當你想要直接修改陣列中的元素時。
splice() 會根據 3 個參數 - 起始點、欲刪除元素數量、欲添加元素,來決定如何修改原陣列。
splice(start)
splice(start, deleteCount)
splice(start, deleteCount, ...itemN)
splice() 可接受 3 個以上的參數,前 2 個分別是 start 及 deleteCount,之後便是 0 至多個 ...itemN。
在以下的描述中,陣列長度都會使用 length 來表示。
startsplice() 用來修改陣列的起始點,也就是準備刪除元素的起始點,也是欲添加元素的起始點。
start 的值可以是一個正負數,會有以下狀況:
length 則視為 length,也就表示不會有元素被刪除,且新元素會從陣列最末端開始添加。length + start,因此 -1 會等於陣列的最後一個元素,以此類推...。如果相加後仍小於 0 則視為 0。1.2 -> 1、 -1.2 -> -1
deleteCount欲刪除的元素數量,會從 start 開始刪除指定數量的元素。
如果不提供、或其值 ≥ length - start (表示刪除的數量大於等於從 start 開始計算的數量),因此從 start 開始之後 (包括 start) 的元素將被全部刪除。
如果其值為 0 或 負數,則不會有任何元素被移除。
...itemN欲添加的新元素,會從 start 開始添加新的元素。
如果沒有提供,則 splice() 只會從陣列中移除元素,不會添加元素。
splice() 會在修改原陣列後回傳被刪除的元素陣列。
如果沒有元素被刪除,則回傳空陣列。
會改動到原陣列。
splice() 會跟據給予的參數修改原陣列,也就是說它會改變 this 所指向的物件,一般來說會是呼叫它的陣列。
如果新增加的元素數量跟刪除的元素數量不一樣時,陣列的 length 會被改變,這點是必要的,因為如果 splice() 沒有隨之改變 length 將會產生非預期的 empty slot,詳細可以參考 Day 27 的介紹。
splice() 會使用到 @@species 這個 well-known Symbol 來創造一個新陣列,並將其回傳,詳細可參考 Day 27 的介紹。
splice() 如果移除了稀疏陣列的 empty slot,那回傳的新陣列也會包含被移除的 empty slot。
const names = ['Cate', 'Emma', 'Alejo', 'Russ']
console.log(names.splice(1, 1))
// ['Emma']
console.log(names)
// ['Cate', 'Alejo', 'Russ']
console.log(names.splice(1, 0, 'Emma'))
// []
console.log(names)
// ['Cate', 'Emma', 'Alejo', 'Russ']
console.log(names.splice(1, 1, 'Pedro'))
// ['Emma']
console.log(names)
// ['Cate', 'Pedro', 'Alejo', 'Russ']
console.log(names.splice(4, 0, 'Anita'))
// []
console.log(names)
// ['Cate', 'Pedro', 'Alejo', 'Russ', 'Anita']
注意原陣列一直不斷地被改動!
start 使用負數、小數或超出範圍const names = ['Cate', 'Emma', 'Alejo', 'Russ']
console.log([...names].splice(1.1, 1))
// ['Emaa']
console.log([...names].splice(2.7, 1))
// ['Ajejo']
console.log([...names].splice(-1, 1))
// ['Russ']
console.log([...names].splice(-1.9, 1))
// ['Russ']
console.log([...names].splice(4, 2, 'newOne'))
// []
console.log([...names].splice(-7, 1))
// ['cate']
這邊使用展開運算子 (spread operator, ...) 先複製原陣列再進行操作,以免改動到原陣列。
const names = ['Cate', 'Emma', 'Alejo', 'Russ']
// 1. with map()
console.log(names.map(item => item).splice(1, 1))
// ['Emma']
console.log(names)
// ['Cate', 'Emma', 'Alejo', 'Russ']
// 2. with spread syntax
console.log([...names].splice(1, 1))
// ['Emma']
console.log(names)
// ['Cate', 'Emma', 'Alejo', 'Russ']
// 3. with filter()
console.log(names.filter(() => true).splice(1, 1))
// ['Emma']
console.log(names)
// ['Cate', 'Emma', 'Alejo', 'Russ']
// 4. with slice()
console.log(names.slice().splice(1, 1))
// ['Emma']
console.log(names)
// ['Cate', 'Emma', 'Alejo', 'Russ']
// 5. with concat()
console.log(names.concat().splice(1, 1))
// ['Emma']
console.log(names)
// ['Cate', 'Emma', 'Alejo', 'Russ']
// 6. with reduce()
console.log(names.reduce((duplicate, name) => {
	duplicate.push(name)
	return duplicate
}, []).splice(1, 1))
// ['Emma']
console.log(names)
// ['Cate', 'Emma', 'Alejo', 'Russ']
// 7. with Array.from()
console.log(Array.from(names).splice(1, 1))
// ['Emma']
console.log(names)
// ['Cate', 'Emma', 'Alejo', 'Russ']
為了保持 immutable 的原則,我們應該儘量不改動原陣列!這邊使用了 7 種方法來淺拷貝 (shallow copy) 原陣列。
const fakeArray = {
	0: 0,
	1: 1,
	2: 2,
	notMyBussiness: 'Hi!',
	length: 4
}
console.log([].splice.call(fakeArray, 1, 1, 'newOne'))
// [1]
console.log(fakeArray)
// {0: 0, 1: 'newOne', 2: 2, notMyBussiness: 'Hi!', length: 4}
splice() 被刻意做成通用的咩色,因此有著 length 屬性的類陣列物件也可以使用。
reverse() 被刻意做成通用的,擁有 length 屬性的類陣列物件都可以使用這個咩色;但要注意的是, 字串雖然也是類陣列的一種,但由於它是不可改變的(Immutable ),因此並沒有辦法適用這個咩色,我們來驗證一下:

start 跟 deleteCount 都可以使用小數,splice() 會自動將其轉換成整數,但有可能會跟預期的不一樣,詳細可參考 Day 27 的介紹。
由於 splice() 會直接改動原陣列,如果想要遵守 immutable 的原則,可以先對原陣列做淺拷貝 (shallow) 再使用 splice();如果組合其他咩色可以達到相同的目的 (例如 slice),那可以考慮不使用這類會直接變動原陣列的咩色,可以參考範例的 Example 2。
由於 splice() 的篇幅較為龐大,所以這邊將 ECMAScript 的部分獨立出來,我們會於明天 (Day 27) 做介紹,虱目魚 die 吧!
最後,希望大家可以開心地使用各種咩色,體驗它帶給你的便利,祝大家歸剛沒煩惱。