模擬實(shí)現(xiàn) Array.prototype.splice
splice
array.splice(start[,?deleteCount[,?item1[,?item2[,?...]]]])MDN:splice()?方法通過(guò)刪除或替換現(xiàn)有元素或者原地添加新的元素來(lái)修改數(shù)組,并以數(shù)組形式返回被修改的內(nèi)容。此方法會(huì)改變?cè)瓟?shù)組
Array.prototype.splice() 的用法如下:
array.splice(start):刪除數(shù)組中從下標(biāo)start開(kāi)始(包含start)的所有元素array.splice(start, deleteCount):刪除數(shù)組中從下標(biāo)start開(kāi)始(包含start)的deleteCount元素array.splice(start, deleteCount, item1, item2, ...):刪除數(shù)組中從下標(biāo)start開(kāi)始(包含start)的deleteCount元素,然后在相同位置上插入item1, item2, ...
特征包括如下:
start:可正可負(fù),正數(shù)表示從下標(biāo)為start的位置開(kāi)始修改,如果start > array.length - 1,則表示從數(shù)組末尾處開(kāi)始修改;負(fù)數(shù)表示從數(shù)組末位開(kāi)始的第幾位(從-1計(jì)數(shù),這意味著-n是倒數(shù)第n個(gè)元素并且等價(jià)于array.length-n)deleteCount:表示從start開(kāi)始要移除的元素個(gè)數(shù),省略則表示把start之后的所有元素都移除,如果是0或負(fù)數(shù),則不移除元素item1, item2, ...:要添加進(jìn)數(shù)組的元素,從start?位置開(kāi)始。如果不指定,則?splice()?將只刪除數(shù)組元素返回:被刪除的元素組成的一個(gè)數(shù)組
實(shí)現(xiàn)思路:
處理
start負(fù)數(shù)或超出邊界問(wèn)題,計(jì)算真實(shí)有效的開(kāi)始位置startIndex處理
deleteCount負(fù)數(shù)問(wèn)題,計(jì)算真實(shí)有效的刪除元素個(gè)數(shù)delCount從
startIndex開(kāi)始刪除delCount個(gè)元素并原地添加item1, item2, …(添加元素個(gè)數(shù)為addCount)


如果 delCount < addCount(刪除的元素個(gè)數(shù)小于添加元素):將數(shù)組中startIndex + delCount后的元素向后移動(dòng)addCount - delCount個(gè)位置,將元素拷貝進(jìn)來(lái)如果 delCount = addCount(刪除的元素個(gè)數(shù)等于添加元素):直接將添加元素覆蓋刪除元素即可拷貝刪除的 delCount到新數(shù)組deletedElements,用于array.splice函數(shù)返回如果 delCount > addCount(刪除的元素個(gè)數(shù)大于添加元素):將數(shù)組中startIndex + delCount后的元素向前移動(dòng)delCount - addCount個(gè)位置,將添加元素拷貝進(jìn)來(lái)返回
deletedElements
代碼實(shí)現(xiàn):
Array.prototype._splice?=?function(start,?deleteCount)?{
????//?入?yún)⒃貍€(gè)數(shù)
????let?argumentsLen?=?arguments.length
????//?數(shù)組
????let?array?=?Object(this)
????//?數(shù)組長(zhǎng)度
????let?len?=?array.length
????//?添加元素個(gè)數(shù)
????let?addCount?=?argumentsLen?>?2???argumentsLen?-2?:?0
????//?計(jì)算有效的?start
????let?startIndex?=?computeSpliceStartIndex(start,?array)
????//?計(jì)算有效的?deleteCount
????let?delCount?=?computeSpliceDeleteCount(startIndex,?deleteCount,?len)
????//?記錄刪除的數(shù)組元素
????let?deletedElements?=?new?Array(delCount)
????
????//?將待刪除元素記錄到?deletedArray
????recordDeleteElements(startIndex,?delCount,?array,?deletedElements)
????
????//?移動(dòng)數(shù)組元素
????moveElements(startIndex,?delCount,?array,?addCount)
????
????let?i?=?startIndex
????let?argumentsIndex?=?2
????
????//?插入新元素
????while?(argumentsIndex?????????array[i++]?=?arguments[argumentsIndex++]
????}
????
????array.length?=?len?-?delCount?+?addCount
????//?返回刪除元素?cái)?shù)組
????return?deletedElements;
}
//?計(jì)算真實(shí)的?start
function?computeSpliceStartIndex(start,?len)?{
????//?處理負(fù)值,如果負(fù)數(shù)的絕對(duì)值大于數(shù)組的長(zhǎng)度,則表示開(kāi)始位置為第0位
????if(start?0)?{
????????start?+=?len
????????return?start?0???0?:?start
????}
????//?處理超出邊界問(wèn)題
????return?start?>?len?-?1???len?-?1:?start
}?
//?計(jì)算真實(shí)的?deleteCount
function?computeSpliceDeleteCount(startIndex,?deleteCount,?len)?{
????//?超出邊界問(wèn)題
????if(deleteCount?>?len?-?startIndex)?deleteCount?=?len?-?startIndex
????//?負(fù)值問(wèn)題
????if(deleteCount?0)?deleteCount?=?0
????return?deleteCount
}
//?記錄刪除元素,用于?Array.prototype.splice()?返回
function?recordDeleteElements(startIndex,?delCount,?array,?deletedElementd)?{
????for(let?i?=?0;?i?????????deletedElementd[i]?=?array[startIndex?+?i]
????}
}
//?移動(dòng)數(shù)組元素,便于插入新元素
function?moveElements(startIndex,?delCount,?array,?addCount)?{
????let?over?=?addCount?-?delCount
????if(over)?{
????????//?向后移
????????for(let?i?=?array.length?-?1;?i?>=?startIndex?+?delCount;?i--)?{
????????????array[i+over]?=?array[i]
????????}
????}?else?if?(over?0)?{
????????//?向前移
????????for(let?i?=?startIndex?+?delCount;?i?<=?array.length?-?1;?i++)?{
????????????if(i?+?Math.abs(over)?>?array.length?-?1)?{
????????????????//?刪除冗于元素
????????????????delete?array[i]
????????????????continue
????????????}
????????????array[i]?=?array[i?+?Math.abs(over)]
????????}
????}
}
let?months?=?['Jan',?'March',?'April',?'June']
console.log(months._splice(1,?1,?'Feb'))
//?["March"]
console.log(months)
//?["Jan",?"Feb",?"April",?"June"]
補(bǔ)充:密封對(duì)象與凍結(jié)對(duì)象
密封對(duì)象:通常一個(gè)對(duì)象是可擴(kuò)展的(可以添加新的屬性)。密封一個(gè)對(duì)象會(huì)讓這個(gè)對(duì)象變的不能添加新屬性,且所有已有屬性會(huì)變的不可配置。屬性不可配置的效果就是屬性變的不可刪除,以及一個(gè)數(shù)據(jù)屬性不能被重新定義成為訪(fǎng)問(wèn)器屬性,或者反之。但屬性的值仍然可以修改。嘗試刪除一個(gè)密封對(duì)象的屬性或者將某個(gè)密封對(duì)象的屬性從數(shù)據(jù)屬性轉(zhuǎn)換成訪(fǎng)問(wèn)器屬性,結(jié)果會(huì)靜默失敗或拋出
TypeError(在嚴(yán)格模式?中最常見(jiàn)的,但不唯一)--MDN
if(delCount?!==?addCount?&&?Object.isSealed(array))?{
????throw?new?TypeError('the?array?is?sealed')
}
凍結(jié)對(duì)象:數(shù)組作為一種對(duì)象,被凍結(jié),其元素不能被修改。沒(méi)有數(shù)組元素可以被添加或移除。
--MDN
if(delCount?>?0?&&?addCount?>?0?&&?Object.isFrozen(array))?{
????throw?new?TypeError('the?array?is?frozen')
}
V8源碼
function?ArraySplice(start,?delete_count)?{
??CHECK_OBJECT_COERCIBLE(this,?"Array.prototype.splice");
????
??//?參數(shù)
??var?num_arguments?=?arguments.length;
??//?數(shù)組
??var?array?=?TO_OBJECT(this);
??//?數(shù)組長(zhǎng)度
??var?len?=?TO_LENGTH(array.length);
??//?計(jì)算有效的?start_i
??var?start_i?=?ComputeSpliceStartIndex(TO_INTEGER(start),?len);
??//?計(jì)算有效的?del_count
??var?del_count?=?ComputeSpliceDeleteCount(delete_count,?num_arguments,?len,
???????????????????????????????????????????start_i);
??//?返回?cái)?shù)組
??var?deleted_elements?=?ArraySpeciesCreate(array,?del_count);
??deleted_elements.length?=?del_count;
??//?添加元素個(gè)數(shù)
??var?num_elements_to_add?=?num_arguments?>?2???num_arguments?-?2?:?0;
????
??//?如果是密封對(duì)象且刪除元素個(gè)數(shù)與添加元素個(gè)數(shù)不一致,報(bào)錯(cuò)
??if?(del_count?!=?num_elements_to_add?&&?%object_is_sealed(array))?{
????throw?%make_type_error(kArrayFunctionsOnSealed);
??}?else?if?(del_count?>?0?&&?%object_is_frozen(array))?{
????//?如果是凍結(jié)對(duì)象且刪除元素個(gè)數(shù)大于0,報(bào)錯(cuò)
????throw?%make_type_error(kArrayFunctionsOnFrozen);
??}
????
??//?計(jì)算變更元素
??var?changed_elements?=?del_count;
??if?(num_elements_to_add?!=?del_count)?{
????//?If?the?slice?needs?to?do?a?actually?move?elements?after?the?insertion
????//?point,?then?include?those?in?the?estimate?of?changed?elements.
????changed_elements?+=?len?-?start_i?-?del_count;
??}
????
??//?移動(dòng)元素
??if?(UseSparseVariant(array,?len,?IS_ARRAY(array),?changed_elements))?{
????%NormalizeElements(array);
????if?(IS_ARRAY(deleted_elements))?%NormalizeElements(deleted_elements);
????SparseSlice(array,?start_i,?del_count,?len,?deleted_elements);
????SparseMove(array,?start_i,?del_count,?len,?num_elements_to_add);
??}?else?{
????SimpleSlice(array,?start_i,?del_count,?len,?deleted_elements);
????SimpleMove(array,?start_i,?del_count,?len,?num_elements_to_add);
??}
??//?將添加元素插入到刪除元素的位置
??var?i?=?start_i;
??var?arguments_index?=?2;
??var?arguments_length?=?arguments.length;
??while?(arguments_index?????array[i++]?=?arguments[arguments_index++];
??}
??array.length?=?len?-?del_count?+?num_elements_to_add;
??//?Return?the?deleted?elements.
??return?deleted_elements;
}
Array.prototype.splice源碼地址
總結(jié)
splice 實(shí)現(xiàn)原理很簡(jiǎn)單,核心就是移動(dòng)刪除元素的邊界,使無(wú)效元素個(gè)數(shù)與添加元素個(gè)數(shù)一致,然后用添加元素覆蓋進(jìn)去, 注意 ?splice 是原地操作,不創(chuàng)建新數(shù)組,需要判讀數(shù)組是否被密封或凍結(jié)
完整代碼實(shí)現(xiàn)
Array.prototype._splice?=?function(start,?deleteCount)?{
????//?入?yún)⒃貍€(gè)數(shù)
????let?argumentsLen?=?arguments.length
????//?數(shù)組
????let?array?=?Object(this)
????//?數(shù)組長(zhǎng)度
????let?len?=?array.length
????//?添加元素個(gè)數(shù)
????let?addCount?=?argumentsLen?>?2???argumentsLen?-2?:?0
????//?計(jì)算有效的?start
????let?startIndex?=?computeSpliceStartIndex(start,?array)
????//?計(jì)算有效的?deleteCount
????let?delCount?=?computeSpliceDeleteCount(startIndex,?deleteCount,?len)
????//?記錄刪除的數(shù)組元素
????let?deletedElements?=?new?Array(delCount)
????
????//?將待刪除元素記錄到?deletedArray
????recordDeleteElements(startIndex,?delCount,?array,?deletedElements)
????
????//?密封對(duì)象
????if(delCount?!==?addCount?&&?Object.isSealed(array))?{
????????throw?new?TypeError('the?array?is?sealed')
????}
????//?凍結(jié)對(duì)象
????if(delCount?>?0?&&?addCount?>?0?&&?Object.isFrozen(array))?{
????????throw?new?TypeError('the?array?is?frozen')
????}
????
????//?移動(dòng)數(shù)組元素
????moveElements(startIndex,?delCount,?array,?addCount)
????
????let?i?=?startIndex
????let?argumentsIndex?=?2
????
????//?插入新元素
????while?(argumentsIndex?????????array[i++]?=?arguments[argumentsIndex++]
????}
????
????array.length?=?len?-?delCount?+?addCount
????//?返回刪除元素?cái)?shù)組
????return?deletedElements;
}
//?計(jì)算真實(shí)的?start
function?computeSpliceStartIndex(start,?len)?{
????//?處理負(fù)值,如果負(fù)數(shù)的絕對(duì)值大于數(shù)組的長(zhǎng)度,則表示開(kāi)始位置為第0位
????if(start?0)?{
????????start?+=?len
????????return?start?0???0?:?start
????}
????//?處理超出邊界問(wèn)題
????return?start?>?len?-?1???len?-?1:?start
}?
//?計(jì)算真實(shí)的?deleteCount
function?computeSpliceDeleteCount(startIndex,?deleteCount,?len)?{
????//?超出邊界問(wèn)題
????if(deleteCount?>?len?-?startIndex)?deleteCount?=?len?-?startIndex
????//?負(fù)值問(wèn)題
????if(deleteCount?0)?deleteCount?=?0
????return?deleteCount
}
//?記錄刪除元素,用于?Array.prototype.splice()?返回
function?recordDeleteElements(startIndex,?delCount,?array,?deletedElementd)?{
????for(let?i?=?0;?i?????????deletedElementd[i]?=?array[startIndex?+?i]
????}
}
//?移動(dòng)數(shù)組元素,便于插入新元素
function?moveElements(startIndex,?delCount,?array,?addCount)?{
????let?over?=?addCount?-?delCount
????if(over)?{
????????//?向后移
????????for(let?i?=?array.length?-?1;?i?>=?startIndex?+?delCount;?i--)?{
????????????array[i+over]?=?array[i]
????????}
????}?else?if?(over?0)?{
????????//?向前移
????????for(let?i?=?startIndex?+?delCount;?i?<=?array.length?-?1;?i++)?{
????????????if(i?+?Math.abs(over)?>?array.length?-?1)?{
????????????????//?刪除冗于元素
????????????????delete?array[i]
????????????????continue
????????????}
????????????array[i]?=?array[i?+?Math.abs(over)]
????????}
????}
}
const?months?=?['Jan',?'March',?'April',?'June']
console.log(months._splice(1,?0,?'Feb'))
//?[]
console.log(months)
//?["Jan",?"Feb",?"March",?"April",?"June"]
console.log(months._splice(4,?1,?'May'))
//?["June"]
console.log(months)
//?["Jan",?"Feb",?"March",?"April",?"May"]
