比较 javascript 中 filter 和 splice 删除数组的性能

前言

因为最近在项目中经常需要将数组中的一个元素从数组中剔除,基于 ES6 的选择有比较多, 产生了一个关于几种方法之间执行效率的疑惑,网上也没有太多资料,于是乎本着学习性能测试的想法,展开了一段性能测试的旅程

测试工具

  • benchmark 性能测试核心依赖
  • mockjs 生成假数据
  • lodash 提供另外的筛选数组的方法用于参照
1
yarn add benchmark mockjs lodash

测试思路

首先定义四种方法来测试数据删除操作,他们分别是 Array.prototype.filter Array.prototype.splice lodash.reject lodash.filter

然后使用 mockjs 生成两个数组,长度分别是 100 和 1000,用四种方法分别对这两组数据进行测试。
需要注意的是,在测试时需要对测试数组进行深拷贝,避免他们之间相互影响(毕竟 Array.prototype.splice 会改变原数组)

然后我们对其中一组数据进行测试,来校验我们写的方法是否正确

最后运行 benchmark,比较结果得出结论

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// benchmark.js

// 引入相关依赖
const _ = require('lodash')
const { mock, Random } = require('mockjs')
const Benchmark = require('benchmark')
const suite = new Benchmark.Suite()

// Array.prototype.filter
function fun1 (array, id) {
return array.filter(item => item.id !== id)
}
// Array.prototype.splice
function fun2 (array, id) {
const index = array.map(item => item.id).indexOf(id)
array.splice(index, 1)
return array
}
// lodash.reject
function fun3 (array, id) {
return _.reject(array, item => item.id === id)
}
// lodash.filter
function fun4 (array, id) {
return _.filter(array, item => item.id !== id)
}

// 生成两组假数据
console.time('generate data')
const data100 = mock({ 'array|100': [{ 'id|+1': 1, 'name': Random.name(), 'content': Random.paragraph() }] }).array
const data1000 = mock({ 'array|1000': [{ 'id|+1': 1, 'name': Random.name(), 'content': Random.paragraph() }] }).array
console.timeEnd('generate data')

// 校验结果
console.time('test')
for (let i = 1; i <= 4; i++) {
const result = eval(`fun${i}([...data100], 2)`)
if (result[1].id !== 3 || result.length !== 99) throw new Error(`fun${i} test failed.`)
}
console.timeEnd('test')

// 开测
suite
.add('Array.prototype.filter 100', () => {
fun1(data100, 50)
})
.add('Array.prototype.splice 100', () => {
fun2(data100, 50)
})
.add('lodash.reject 100', () => {
fun3(data100, 50)
})
.add('lodash.filter 100', () => {
fun4(data100, 50)
})
.add('Array.prototype.filter 1000', () => {
fun1(data1000, 500)
})
.add('Array.prototype.splice 1000', () => {
fun2(data1000, 500)
})
.add('lodash.reject 1000', () => {
fun3(data1000, 500)
})
.add('lodash.filter 1000', () => {
fun4(data1000, 500)
})
.on('cycle', event => {
console.log(String(event.target).padStart(80))
})
.on('complete', function () {
console.log('Faster is ' + this.filter('fastest').map('name'))
})
.run({
async: true,
})

运行测试

1
node benchmark.js

测试结果

generate data: 24.946ms
test: 1.099ms

      Array.prototype.filter 100 x 1,917,735 ops/sec ±0.77% (89 runs sampled)
     Array.prototype.splice 100 x 12,774,324 ops/sec ±0.54% (90 runs sampled)
              lodash.reject 100 x 26,526,046 ops/sec ±0.64% (91 runs sampled)
              lodash.filter 100 x 47,850,218 ops/sec ±0.91% (91 runs sampled)

       Array.prototype.filter 1000 x 169,970 ops/sec ±6.32% (92 runs sampled)
    Array.prototype.splice 1000 x 12,802,157 ops/sec ±0.71% (86 runs sampled)
             lodash.reject 1000 x 26,534,300 ops/sec ±0.63% (92 runs sampled)
             lodash.filter 1000 x 47,920,887 ops/sec ±0.55% (93 runs sampled)

Faster is lodash.filter 1000,lodash.filter 100

结论

  • Array.prototype.filter 最最最最最最慢
  • Array.prototype.splice 略慢
  • lodash.reject 略快
  • lodash.filter 最快

在数据量较大时,Array.prototpe.filter 方法会随着数组长度越来越慢,其他几种则似乎不太受影响。

在项目不使用 lodash 依赖或数据量较小时,可优先考虑 Array.prototype.splice 方法,不过要注意,Array.prototype.splice 会改变原数组

也不知道 lodash 内部用了什么黑科技,比 js 原生的 splice 还要快,有机会研究一下 lodash 源码,忙里偷闲写了篇文章,先继续工作了