ES6 Proxy 性能之我见

本文翻译自https://thecodebarbarian.com/thoughts-on-es6-proxies-performance

Proxy是ES6的一个强力功能,它通过为 get/set一个属性 设置"陷阱"(函数处理器)让我们可以拦截对于对象属性的操作。比如:

const obj = {};
const proxy = new Proxy(obj, {get: () => {console.log('hi');}
});obj.a; // "hi"

Proxy被称赞为现在已经被废弃的Object.observe()属性的取代者。

然而不幸的是,Proxy有一个致命缺陷:性能。

更打击人的是,Object.observe()就是因为性能被废弃的,而以我(原作者)对V8的理解,对于JIT(Just in Time,准时制)来说,Object.observe()比Proxy容易优化多了。

Proxy到底有多慢?

我(原作者)在node v6.9.0中用benchmark简单试了一下:

var Benchmark = require('benchmark');var suite = new Benchmark.Suite;var obj = {};var _obj = {};
var proxy = new Proxy(_obj, {set: (obj, prop, value) => { _obj[prop] = value; }
});var defineProp = {};
Object.defineProperty(defineProp, 'prop', {configurable: false,set: v => defineProp._v = v
});// 译者注: vanilla js 指的就是原生js
suite.add('vanilla', function() {obj.prop = 5;}).add('proxy', function() {proxy.prop = 5;}).add('defineProperty', function() {defineProp.prop = 5;}).on('cycle', function(event) {console.log(String(event.target));}).on('complete', function() {console.log('Fastest is ' + this.filter('fastest').map('name'));}).run();

结果如下:

$ node proxy.js
vanilla x 74,288,023 ops/sec ±0.78% (86 runs sampled)
proxy x 3,625,152 ops/sec ±2.51% (86 runs sampled)
defineProperty x 74,815,513 ops/sec ±0.80% (85 runs sampled)
Fastest is defineProperty,vanilla
$

从这个简单的benchmark中我们可以看到,Proxy的set 比直接赋值和defineProperty慢非常多(译者注:ops/sec,每秒进行的操作数,越大越快)。

为防大家好奇,我(原作者)又在node 4.2.1测试了一下Object.observe()

$ node proxy.js
vanilla x 78,615,272 ops/sec ±1.55% (84 runs sampled)
defineProperty x 79,882,188 ops/sec ±1.31% (85 runs sampled)
Object.observe() x 5,234,672 ops/sec ±0.86% (89 runs sampled)
Fastest is defineProperty,vanilla

有些文章可能让你觉得只要Proxy不用get/set而是只设置getOwnPropertyDescriptor()的话,就比其他的快,于是我(原作者)又试了试:

var _obj = {};
var propertyDescriptor = {configurable: true,set: v => { _obj.prop = v; }
};
var proxy = new Proxy(_obj, {getOwnPropertyDescriptor: (target, prop) => propertyDescriptor
});

不幸的是,反而更慢了:

$ node proxy.js
vanilla x 73,695,484 ops/sec ±1.04% (88 runs sampled)
proxy x 2,026,006 ops/sec ±0.74% (90 runs sampled)
defineProperty x 74,137,733 ops/sec ±1.25% (88 runs sampled)
Fastest is defineProperty,vanilla
$

用Proxy包裹一个函数并调用同样比原生的包裹函数并调用慢非常多:

var Benchmark = require('benchmark');var suite = new Benchmark.Suite;var fn = () => 5;
var proxy = new Proxy(function() {}, {apply: (target, context, args) => fn.apply(context, args)
});var wrap = () => fn();// add tests
suite.add('vanilla', function() {fn();}).add('proxy', function() {proxy();}).add('wrap', function() {wrap();}).on('cycle', function(event) {console.log(String(event.target));}).on('complete', function() {console.log('Fastest is ' + this.filter('fastest').map('name'));}).run();
$ node proxy2.js
vanilla x 78,426,813 ops/sec ±0.93% (88 runs sampled)
proxy x 5,244,789 ops/sec ±2.17% (87 runs sampled)
wrap x 75,350,773 ops/sec ±0.85% (85 runs sampled)
Fastest is vanilla

无用的提升Proxy性能的方法

目前最有影响力的提升Proxy性能的方法是让被修改的属性的configurable设为false:

var _obj = {};
Object.defineProperty(_obj, 'prop', { configurable: false });
var propertyDescriptor = {configurable: false,enumerable: true,set: v => { _obj.prop = v; }
};
var proxy = new Proxy(_obj, {getOwnPropertyDescriptor: (target, prop) => propertyDescriptor
});

(译者注:这段代码有些问题,enumerableconfigurable为false时是无效的)

$ node proxy.js
vanilla x 74,622,163 ops/sec ±0.95% (85 runs sampled)
proxy x 4,649,544 ops/sec ±0.47% (85 runs sampled)
defineProperty x 77,048,878 ops/sec ±0.60% (88 runs sampled)
Fastest is defineProperty
$

要是这样写set/get,还不如直接用 Object.defineProperty()

这样写的话,你就不得不设置每个你要在Proxy中用到的属性不可配置(not configurable)。

不然的话,V8就会报错:

var _obj = {};
Object.freeze(_obj);
var propertyDescriptor = {configurable: false,enumerable: true,set: v => { _obj.prop = v; }
};
var proxy = new Proxy(_obj, {getOwnPropertyDescriptor: (target, prop) => propertyDescriptor
});// Throws:
// "TypeError: 'getOwnPropertyDescriptor' on proxy: trap returned
// descriptor for property 'prop' that is incompatible with the
// existing property in the proxy target"
// 拦截'prop'属性返回的descriptor和target(原对象)已经存在的属性不匹配
proxy.prop = 5;

Proxy 也要不行了么?

Proxy比 Object.defineProperty()有不少优点:

  • Proxy 可以嵌套,而Object.defineProperty()getter/setter就不能嵌套,这样你就不需要知道提前知道你要拦截的所有属性
  • 可以拦截数组变化

但它性能太差了。

性能有多大影响呢?

以Promise和回调为例:

var Benchmark = require('benchmark');var suite = new Benchmark.Suite;var handleCb = cb => cb(null);// add tests
suite.add('new function', function() {handleCb(function(error, res) {});}).add('new promise', function() {return new Promise((resolve, reject) => {});}).add('promise resolve', function() {Promise.resolve().then(() => {});}).on('cycle', function(event) {console.log(String(event.target));}).on('complete', function() {console.log('Fastest is ' + this.filter('fastest').map('name'));}).run();
$ node promise.js
new function x 26,282,805 ops/sec ±0.74% (90 runs sampled)
new promise x 1,953,037 ops/sec ±1.02% (86 runs sampled)
promise resolve x 194,173 ops/sec ±13.80% (61 runs sampled)
Fastest is new function
$

Promise也慢了非常多,

但是 bluebird声称为Promise提供"非常好的性能",测试一下:

$ node promise.js
new function x 26,986,342 ops/sec ±0.48% (89 runs sampled)
new promise x 11,157,758 ops/sec ±1.05% (87 runs sampled)
promise resolve x 671,079 ops/sec ±27.01% (18 runs sampled)
Fastest is new function

虽然快了很多,但仍然比回调慢不少。

所以我们要因此放弃Promise么?

并不是这样的,很多公司仍然选择了使用Promise。我(原作者)虽然不是很确定,但是Uber好像就在使用Promise。

结论

Proxy很慢,但是在你因其性能而放弃它之前,记得同样性能很差的Promise在最近几年中被快速采用。

如果你想使用代理,很可能你不会感觉到性能的影响,除非你发现自己为了性能的原因改变了Promise库(或者完全避开了它们)。

更新

2019.01, 在node v11.3.0中: Promise已经变得足够好, Proxy还是那样

vanilla x 833,244,386 ops/sec ±0.76% (89 runs sampled)
proxy x 28,590,800 ops/sec ±0.72% (88 runs sampled)
wrap x 824,349,552 ops/sec ±0.87% (86 runs sampled)
Fastest is vanilla,wrap
new function x 834,121,566 ops/sec ±0.82% (89 runs sampled)
new promise x 819,789,350 ops/sec ±0.76% (87 runs sampled)
promise resolve x 1,212,009 ops/sec ±40.98% (30 runs sampled)
Fastest is new function

转载于:https://www.cnblogs.com/zmj97/p/10954968.html

ES6 Proxy 性能之我见相关推荐

  1. mysql proxy性能差_mysql性能的检查和优化方法

    mysql在遇到严重性能问题时,一般都有这么几种可能: 1.索引没有建好; 2.sql写法过于复杂; 3.配置错误; 4.机器实在负荷不了; 1.索引没有建好 如果看到mysql消耗的cpu很大,可以 ...

  2. es6 --- Proxy的属性(get、set除外)

    apply(): 拦截函数的调用.call和apply操作 var target = function () { return 'I am the target';}; var handler = { ...

  3. es6 --- Proxy实例的get方法

    写一个拦截函数,访问目标对象不存在属性时,会抛出不存在该属性的错误 如果存在该属性时,就返回其值. var person = {name: "张三" };var proxy = n ...

  4. 第十二节:ES6 Proxy代理 和 去银行存款有什么关系?

    ES:给开发者提供了一个新特性:Proxy,就是代理的意思.也就是我们这一节要介绍的知识点. 以前,ATM还没有那么流行的时候(暴露年纪),我们去银行存款或者取款的时候,需要在柜台前排队,等柜台工作人 ...

  5. ES6 Proxy和Reflect (上)

    Proxy概述 Proxy用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种"元编程"(meta programming),即对编程语言进行编程. Proxy可以理 ...

  6. es6 Proxy 简介

    Proxy 简介 Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种"元编程"(meta programming),即对编程语言进行编程. Proxy ...

  7. 深入实践 ES6 Proxy Reflect

    原文: https://zhuanlan.zhihu.com/p/60126477 引言 Vue中的数据绑定 Vue作为前端框架的三驾马车之一,在众多前端项目中具有极其重要的作用. Vue中具有一个重 ...

  8. ES6 Proxy 和 Reflect 的理解

    Vue中的数据绑定 ps:观察者模式 (下面有重点) Vue作为前端框架的三驾马车之一,在众多前端项目中具有极其重要的作用. Vue中具有一个重要的功能点--"数据绑定".使用者无 ...

  9. ES6 Proxy和Reflect

    目录 Proxy 概述 基本用法 Proxy 实例方法 1.get(target, propKey, receiver) 2.set(target, propKey, value, receiver) ...

最新文章

  1. 计算机5G英语文献,5G移动网络外文文献翻译2018中英文.docx
  2. Paper Pal:一个中英文论文及其代码大数据搜索平台
  3. qrcode-php生成二维码
  4. .net学习笔记之协变和抗变(原创)
  5. AX2012 常用表关系(客户地址,联系信息)
  6. AutoCAD自定义填充图样详细教程
  7. C++中一维二维数组传参
  8. 常州大学/教务系统/教室相关
  9. Hinton 论文系列《A fast learning algorithm for deep belief nets》
  10. 账户经常被盗号怎么办?防盗“黑科技”了解一下
  11. 论文阅读------Stochastic Gradient Descent with Differentially Private updates
  12. Linux实现ppp拨号4G模块联网全球APN之亚美尼亚(Armenian)
  13. java笔试题含答案总结五
  14. 计算机显卡显示图片原理,认识显卡!浅析显卡及显卡工作原理
  15. 技术中台建设方法和关键设计
  16. java中大数值_Java大数值转换
  17. MAC下SecureCRT 8.0.4破解版远程连接工具的安装与配置
  18. Pycharm使用Database Navigator连接mysql数据库
  19. 数据结构-复杂度(一)
  20. JAP和Spring整合的三种方式详细方法

热门文章

  1. Nginx搭建负载均衡集群
  2. Go 知识点(12) — 类型转换以三方库 cast
  3. Visual Studio 中文显示乱码问题
  4. 算法最少分组法_数据结构
  5. 受用一生的高效 PyCharm 使用技巧(四)
  6. LeetCode简单题之统一一致字符串的数目
  7. LeetCode简单题之赎金信
  8. TVM,Relay,Pass
  9. Battery electric vehicles (BEVs) 快充技术
  10. docker desktop使用入门