# 深入一點 - 爲什麼說splice 效率低呢

原文: https://zswfx.com/articles/5da713302ddd022595ff506a

我們在使用 Array.prototype.splice 方法的時候,都會提及說它速度慢,效率低。尤其在例如 Vue或者React 框架中也不推薦使用,原因是爲什麼呢?

splice 方法

方法介紹如下:

方法也比較明瞭,就是在數組內刪除或者添加元素。
如下示例:

// 添加一個元素
const arr = [1, 2, 3]
arr.splice(1, 0, 2, 3)
// [1, 2, 3, 2, 3]
// 刪除元素
arr.splice(2, 2)
// arr: [1, 2, 3]

返回值則是刪除元素的數組,若是添加就是空數組.

w3c 執行過程

在 w3c 中關於 splice 是如何描述過程的呢?

Array.prototype.splice 位於ecmascript 規範中 15.4 數組章節下面的 15.4.4.12 點擊這了即可

下面看關於 splice描述:

下面就用刪除和添加兩個例子來說明規範的操作過程:

在規範裏面共有 17步的數據操作:

  • 第2步中,splice引入了新組數 A,用來存返回數據結果
  • 第6,7步中,得到真實開始位置與刪除個數,在這裏進行邊界判斷
  • 第9步這裏判斷當前是否是刪除,若 actualDeleteCount > 0即爲刪除,然後得到A的刪除數組,這裏就獲取到了要刪除的元素,若刪除元素個數爲0,則跳過A爲空數組,k 爲0, 否則 k爲刪除個數,A 爲刪除元素集合. actualStart 爲傳入的數組中某個下標值, actualDeleteCount 爲傳入某個個數範圍是 0-len
  • 在12步判斷添加元素是否多與刪除個數,若少於刪除個數,數組長度減少。
  • 第13步判斷添加元素多餘刪除元素個數,數組長度增加。見下圖示
  • 第14步初始化開始位置,也就是變量k
  • 第15步中進行元素位置移動,如果添加則會不斷把元素添加到元素內
  • 第16步中計算length,元素長度是由原始長度減去刪除元素加上增加元素的數量。
  • 最後一步就是返回在第9步中得到移除的元素A.

splice 方法中,我們會使用情況如下:

  • 刪除元素
  • 添加元素
  • 刪除同時添加元素

刪除元素在第9步處理刪除元素數組,第12步處理元素前移並刪除結尾的元素。添加元素在13步內處理元素後移,並在15步在對應下標下放入元素。刪除元素同時刪除上面每一步都會走到。

關於規範一些內部方法說明:
[[HasProperty]](P) 對象上的內部方法,若通過P得到對象結果爲undefined則爲false,否則爲true。
[[GET]](P) 對象內部方法,通過屬性名P獲取結果。
[[Put]](P, V, Throw) 設置對象屬性P的值爲V,Throw若爲true,遇到錯誤則會拋出TypeError。
[[Delete]](P, Throw) 刪除對象上屬性P,若Throw爲true,遇到錯誤則拋出TypeError.

圖示一個數組刪除數量多於添加數量

操作數組:

[0, 1, 2, 3]

這裏進行 splice(1, 2, 4) 操作,從下標1位置移除2個元素,並添加一個元素

圖示一個數組添加多餘刪除個數

[0, 1, 2, 3]

進行 splice(1, 1, 5, 6) 操作,從下標1的位置刪除一個元素,並插入 5和6的示例。

總結

通過上面規範分析和圖示分析,其實splice之所以”慢“, 是因爲每次splice操作除了需要分配新的內存區域去存儲數據外,還需要不斷操作元素的下標,大量移動元素位置,若start=0,豈不是每個元素都需要移動一次呢?這就是說效率不高原因。

tips: splice 會修改數組本身,所以在vue和react中數組數據變化不會導致UI變化的原因之一。

其他參考:

https://www.jianshu.com/p/483...

更好的插入或者刪除方式

上面說splice 用於三種情況:

  • 刪除元素
  • 添加元素
  • 刪除同時添加元素

在最新的ecma中有新的方法可以替代splice用途

刪除數組

使用 filter 代替:

const arr = [1, 2, 3, 4]
// 刪除下標爲2的元素
const newArray = arr.filter((_, index) => index !== 2)
// [1, 2, 4]

添加元素

使用 concat 或者 spread 代替配合 slice 實現任何位置插入:

const array = [1, 2, 3,]
// 下標1 插入10
const newArray = array.slice(0, 1).concat(10, array.slice(1))
// [1, 10, 2, 3]
// const newArray = [...array.slice(0, 1), 10, array.slice(1)]

添加刪除元素

同上

const array = [0, 1, 2, 3, 4, 5]
// 下標位置2位置刪除2個,並插入7,8,9
const newArray = array.slice(0, 2).concat([7, 8, 9], array.slice(4))
// [0, 1, 7, 8, 9, 4, 5]

最後

相信自己,總會有一個辦法解決問題,代碼性能也會一點點提高,歡迎交流。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章