深入一点 - 为什么说splice 效率低呢

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

我们在使用 Array.prototype.splice 方法的时候,都会提及说它速度慢,效率低。尤其在例如 Vue或者React 框架中也不推荐使用,原因是为什么呢?

splice 方法

方法介绍如下:

深入一点 - 为什么说splice 效率低呢_第1张图片

方法也比较明了,就是在数组内删除或者添加元素。
如下示例:

// 添加一个元素
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描述:
深入一点 - 为什么说splice 效率低呢_第2张图片

下面就用删除和添加两个例子来说明规范的操作过程:

在规范里面共有 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个元素,并添加一个元素

深入一点 - 为什么说splice 效率低呢_第3张图片

图示一个数组添加多余删除个数

[0, 1, 2, 3]

进行 splice(1, 1, 5, 6) 操作,从下标1的位置删除一个元素,并插入 5和6的示例。
深入一点 - 为什么说splice 效率低呢_第4张图片

总结

通过上面规范分析和图示分析,其实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]

最后

相信自己,总会有一个办法解决问题,代码性能也会一点点提高,欢迎交流。

你可能感兴趣的:(javascript,javascript专题系列)