原文: 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 点击这了即可
下面就用删除和添加两个例子来说明规范的操作过程:
在规范里面共有 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]
最后
相信自己,总会有一个办法解决问题,代码性能也会一点点提高,欢迎交流。