老有人跑来跟我说 Proxy 和 defineProperty 相比,是性能的巨大提升。我一听,这不对劲啊,跟我学的知识不太一样,我明明记得Proxy 性能比 defineProperty 更差。
所以我就写了几个简单的例子来验证一下。
这个例子的逻辑非常简单,我们在大数据量循环的过程中,分别用 Object.defineProperty 与 Proxy 劫持的数据,执行一次 getter 与 setter。然后利用 performance.now 记录执行时间。
先看 defineProperty 的案例。
首先定义一个简单对象。
// 在循环中,我们会执行计算操作
var target = {
total: 0
}
然后另外定义一个普通变量用于存储劫持过程中访问和设置的值。
然后用 Object.defineProperty 劫持 target。
Object.defineProperty(target, 'count', {
get: function () {
return b;
},
set: function (value) {
b = value;
},
});
然后循环 1000000 次,并打印执行时间。
var total = 0;
var now = performance.now()
for (let index = 0; index < end; index++) {
total += target.count;
target.count = index;
}
console.log('defineProperty', performance.now() - now)
接下来看使用 Proxy 的案例。
也是首先定义一个普通对象。
var target = {
count: 0
}
然后使用 Proxy 代理。
let proxy = new Proxy(target, {
get: (target, prop, receiver) => {
return Reflect.get(target, prop, receiver)
},
set(target, prop, value) {
return Reflect.set(target, prop, value)
}
});
然后循环访问 getter 和 setter。
var total = 0;
var now = performance.now()
for (let index = 0; index < end; index++) {
total += proxy.count;
proxy.count = index;
proxy.count
}
console.log('Proxy', performance.now() - now)
完整代码如下:
<script>
var end = 1000000
var b = 0;
var target = {
count: 0
}
Object.defineProperty(target, 'count', {
get: function () {
return b;
},
set: function (value) {
b = value;
},
});
var total = 0;
var now = performance.now()
for (let index = 0; index < end; index++) {
total += target.count;
target.count = index;
}
console.log('defineProperty', performance.now() - now)
</script>
<script>
var end = 1000000
var target = {
count: 0
}
let proxy = new Proxy(target, {
get: (target, prop, receiver) => {
return Reflect.get(target, prop, receiver)
},
set(target, prop, value) {
return Reflect.set(target, prop, value)
}
});
var total = 0;
var now = performance.now()
for (let index = 0; index < end; index++) {
total += proxy.count;
proxy.count = index;
}
console.log('Proxy', performance.now() - now)
</script>
我的测试电脑如下,性能强悍,对应的浏览器都是最新版。因此这里我们都定义的是 1000000 万次的执行,以更加方便的放大差异。
理论上绝大多数客户的电脑性能都很差,特别是许多面向 B 端的客户,所以如果有条件的朋友可以用客户的环境来做一下测试看看客户电脑上的真实差异
在 chrome 中执行结果为:
我连续执行了 10 次,发现执行结果都相差不大,执行时间上,Proxy 用时更久。
然后我切换浏览器,在 safari 中执行同样的代码,执行结果如下:
结果没想到,在 safari 浏览器中,Proxy 的性能严重低于 defineProperty。
然后我又把代码发给群友,群友用 QQ 浏览器执行了一下。
万万没想到的是,firefox 的执行结果差异最大。
然后我又尝试让 Proxy 代理的对象增加层级,然后进行 set 操作。
注意,这里只是简单的增加对象复杂度,并不代表更深层级的属性也能被代理。
var target = {
count: 0,
b: {
c: 0
}
}
for (let index = 0; index < end; index++) {
total += proxy.count;
proxy.count = index;
proxy.b.c = target.count
}
验证结果发现,当层级变深,执行消耗的时间越长。下图是 chrome 的执行结果。
结论
在常用的几种浏览器中,测试结果比较统一,Proxy 的性能都弱于 defineProperty,在 safari,firefox 中,defineProperty 的性能大幅度领先。
当 Proxy 的目标对象深层次 getter/setter 时,会增加更多的性能损耗。
针对 Proxy 的性能,chrome 优化做得最好。但依然小幅度弱于 defineProperty。
针对于 defineProperty 的性能,firefox 和 safari 做得比较好,大幅度领先其他浏览器。