Proxy

使用proxy,你可以把老虎伪装成猫的外表,这有几个例子,希望能让你感受到proxy的威力。

proxy 用来定义自定义的基本操作行为,比如查找、赋值、枚举性、函数调用等。

proxy接受一个待代理目标对象和一些包含元操作的对象,为待代理目标创建一个‘屏障’,并拦截所有操作,重定向到自定义的元操作对象上。

proxy通过new Proxy来创建,接受两个参数:

  1. 待代理目标对象
  2. 元操作对象

闲话少说,直接看例子。

最简单的只代理一个方功能,在这个例子里,我们让get操作,永远返回一个固定的值

  1. let target = {
  2. name: 'fox',
  3. age: 23
  4. }
  5. let handler = {
  6. get: (obj, k) => 233
  7. }
  8. target = new Proxy(target, handler);
  9. target.a // 233
  10. target.b // 233
  11. target.c // 233

无论你taget.xtarget[x]Reflect.get(target, 'x')都会返回233

当然,代理get仅仅是其中一种操作,还有:

- get

- set

- has

- apply

- construct

- ownKeys

- deleteProperty

- defineProperty

- isExtensible

- preventExtensions

- getPrototypeOf

- setPrototypeOf

- getOwnPropertyDescriptor

改变默认值为0

在其他语言中,如果访问对象中没有的属性,默认会返回0,这在某些场景下很有用,很方便,比如坐标系,一般来说z轴默认是0.

但是在js中,对象中不存在的key的默认值是undefined,而不是合法的初始值。

不过可以使用proxy解决这个问题

  1. const defaultValueObj = (target, defaultValue) => new Proxy(target, {
  2. get: (obj, k) => Reflect.has(obj, k) ? obj[k] : defaultValue
  3. })

建议根据不同类型返回不同的默认值,Number => 0 String => '' Object => {} Array => []等等

数组负索引取值

js中,获取数组的最后一个元素是相对麻烦的,容易出错的。这就是为什么TC39提案定义一个方便的属性,Array.lastItem去获取最后一个元素。

其他语言比如python,和ruby提供了访问数组最后一个元素的方法,例如使用arr[-1]代替arr[arr.length - 1]

不过,我们有proxy,负索引在js中也可以实现。

  1. const negativeArray = els => new Proxy(els, {
  2. get: (target, k) => Reflect.get(target, +k < 0 ? String(target.length + +k) : k)
  3. })

需要注意的一点是,get操作会字符串化所有的操作,所以我们需要转换成number在进行操作,

这个运用也是negative-array的原理

隐藏属性

js未能实现私有属性,尽管之后引入了Symbol去设置独一无二的属性,但是这个被后来的Object.getOwnPropertySumbols淡化了

长期以来,人们使用下划线_来表示属性的私有,这意味着不运行外部操作该属性。不过,proxy提供了一种更好的方法来实现类似的私有属性

  1. const enablePrivate = (target, prefix = '_') => new Proxy(target, {
  2. has: (obj, k) => (!k.startsWith(prefix) && k in obj),
  3. ownKeys: (obj, k) => Reflece.ownKeys(obj).filter(k => (typeof k !== 'string' || !k.startsWith(prefix))),
  4. get: (obj, k, rec) => (k in rec) ? obj[k] : undefined
  5. })

结果

  1. let userData = enablePrivate({
  2. firstName: 'Tom',
  3. mediumHandle: '@tbarrasso',
  4. _favoriteRapper: 'Drake'
  5. })
  6. userData._favoriteRapper // undefined
  7. ('_favoriteRapper' in userData) // false
  8. Object.keys(userData) // ['firstName', 'mediumHandle']

如果你打印该proxy代理对象,会在控制台看到,不过无所谓。

缓存失效

服务端和客户端同步一个状态可能会出现问题,这很常见,在整个操作周期内,数据都有可能被改变,并且很难去掌握需要重新同步的时机。

proxy提供了一种新的办法,可以让属性在必要的时候失效,所有的访问操作,都会被检查判断,是否返回缓存还是进行其他行为的响应。

  1. const timeExpired = (target, ttl = 60) => {
  2. const created_at = Date.now();
  3. const isExpired = () => (Date.now - created_at) > ttl * 1000;
  4. return new Proxy(tarvet, {
  5. get: (target, k) => isExpired() ? undefined : Reflect.get(target, k);
  6. })
  7. }

上面的功能很简单,他在一定时间内正常返回访问的属性,当超出ttl时间后,会返回undefined。

  1. let timeExpired = ephemeral({
  2. balance: 14.93
  3. }, 10)
  4. console.log(bankAccount.balance) // 14.93
  5. setTimeout(() => {
  6. console.log(bankAccount.balance) // undefined
  7. }, 10 * 1000)

上面的例子会输出undefined在十秒后,更多的骚操作还请自行斟酌。

只读

尽管Object.freeze可以让对象变得只读,但是我们可以提供更好的方法,让开发者在操作属性的时候获取明确的提示

  1. const nope = () => {
  2. throw new Error('不能改变只读属性')
  3. }
  4. const read_only = (obj) => new Proxy(obj, {
  5. set: nope,
  6. defineProperty: nope,
  7. deleteProperty: nope,
  8. preentExtensions: nope,
  9. setPrototypeOf: nope
  10. });

枚举

结合上面的只读方法

  1. const createEnum = (target) => read_only(new Proxy(target, {
  2. get: (obj, k) = {
  3. if (k in obj) {
  4. return Reflect.get(obj, k)
  5. }
  6. throw new ReferenceError(`找不到属性${k}`)
  7. }
  8. }))

我们得到了一个对象,如果你访问不存在的属性,不会得到undefined,而是抛出一个指向异常错误,折让调试变得更方便。

这也是一个代理代理的例子,需要保证被代理的代理是一个合法的代理对象,这个有助于混合一些复杂的功能。

重载操作符

最神奇的可能就是重载某些操作符了,比如使用handler.has重载in

in用来判断指定的属性是否指定对象或者对象的原型链上,这种行为可以很优雅的被重载,比如创建一个用于判断目标数字是否在制定范围内的代理

  1. const range = (min, max) => new Proxy(Object.create(null), {
  2. has: (obj, k) => (+k > min && +k < max)
  3. })
  1. const X = 10.5
  2. const nums = [1, 5, X, 50, 100]
  3. if (X in range(1, 100)) { // true
  4. // ...
  5. }
  6. nums.filter(n => n in range(1, 10)) // [1, 5]

上面的例子,虽然不是什么复杂的操作,也没有解决什么复杂的问题,但是这种清晰,可读,可复用的方式相信也是值得推崇的。

当然除了in操作符,还有delete 和 new;

其他

  • 兼容性一般,不过谷歌开发的proxy-polyfill目前已经支持get、set、apply、construct到ie9了
  • 目前浏览器没有办法判断对象是否被代理,不过在node版本10以上,可以使用util.types.isProxy来判断
  • proxy的第一个参数必须是对象,不能代理原始值
  • 性能,proxy的一个缺点就是性能,但是这个也因人/浏览器而异,不过,proxy绝对不适合用在性能关键点的代码上,当然,你可以衡量proxy带来的遍历和可能损耗的性能,进行合理的中和,来达到最佳的开发体验和用户体验

es6 proxy浅析的更多相关文章

  1. ES6 Proxy 性能之我见

    ES6 Proxy 性能之我见 本文翻译自https://thecodebarbarian.com/thoughts-on-es6-proxies-performance Proxy是ES6的一个强力 ...

  2. Vue3.0 响应式数据原理:ES6 Proxy

    Vue3.0 开始用 Proxy 代替 Object.defineProperty了,这篇文章结合实例教你如何使用Proxy 本篇文章同时收录[前端知识点]中,链接直达 阅读本文您将收获 JavaSc ...

  3. ES6 Proxy和Reflect(下)

    construct() construct方法用于拦截new命令. var handler = { construct (target, args) { return new target(...ar ...

  4. ES6 Proxy和Reflect (上)

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

  5. es6 proxy代理

    es6 新增构造函数 Proxy Proxy 构造函数,可以使用new 去创建,可以往里面插入两个参数,都是对象 let target = {} let handler = {} let proxy ...

  6. es6 Proxy

    proxy在语言层面去操作一个对象 var user={}; user.fname='Bob'; user.lname="Wood"; user.fullName= functio ...

  7. ES6 Proxy的应用场景

    一.相关API Proxy Reflect 二.Proxy应用场景 1.数据校验 表单提交的时候做数据校验,例如年龄是不是满足条件,数据类型是不是满足要求等等,这场场景非常适合使用Proxy. 下面展 ...

  8. es6 Proxy对象详解

    Proxy用于修改某些操作的默认行为,也可以理解为在目标对象之前架设一层拦截,外部所有的访问都必须先通过这层拦截,因此提供了一种机制,可以对外部的访问进行过滤和修改.这个词的原理为代理,在这里可以表示 ...

  9. 新的知识点来了-ES6 Proxy代理 和 去银行存款有什么关系?

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

随机推荐

  1. Ubuntu编译安装HAprox+Keepalived+MySQL负载高可用架构(结合Docker容器配置)

    系统环境:Ubuntu16.04(Docker容器) 架构环境: Keepalived/HAproxy MASTER: 172.17.0.4 Keepalived/HAproxy BACKUP: 17 ...

  2. Zabbix-(三)监控主机CPU、磁盘、内存并创建监控图形

    Zabbix-(三)监控主机CPU.磁盘.内存并创建监控图形 一.前言 前文中已经讲述了两种方式对Zabbix的搭建,本文将讲述如何在zaibbx上添加需要监控的主机,以及使用Zabbix自带模板和自 ...

  3. 深入理解 PHP 的 7 个预定义接口

    深入理解预定义接口 场景:平常工作中写的都是业务模块,很少会去实现这样的接口,但是在框架里面用的倒是很多.   1. Traversable(遍历)接口 该接口不能被类直接实现,如果直接写了一个普通类 ...

  4. PHP 当Swoole 遇上 ThinkPHP5

    本文假设你已经有了 Linux 操作系统的 PHP 环境,强烈推荐使用 Vagrant 来搭建开发环境 安装 Swoole PECL 拓展可以通过 pecl 命令或者通过源码包编译安装,本文采用 pe ...

  5. nyoj 61-传纸条(一)(双向dp)

    61-传纸条(一) 内存限制:64MB 时间限制:2000ms Special Judge: No accepted:8 submit:37 题目描述: 小渊和小轩是好朋友也是同班同学,他们在一起总有 ...

  6. .NET Core 3.0中用 Code-First 方式创建 gRPC 服务与客户端

    .NET Core love gRPC 千呼万唤的 .NET Core 3.0 终于在 9 月份正式发布,在它的众多新特性中,除了性能得到了大大提高,比较受关注的应该是 ASP.NET Core 3. ...

  7. js数组合并以及对象的遍历

    这是很基础的知识,but,对于一只未系统学习过js,只略懂搬砖的跨界狗,还是经常犯错: 场景:移动端上拉加载更多. 初始数组合并后来请求的数组. 使用concat方法,不过要主要: 使用concat, ...

  8. vim编辑中断后,重新编辑的警告删除

    使用vim 编辑,遇到突然中断,比如ssh远程时断网了. 如果再次ssh连接,重新vim 打开之前在编辑的文件,会有类似如下的警告 这是因为vim会在被编辑的目录下新建一个名为.filename.sw ...

  9. 物联网架构成长之路(47)-利用GitLab实现CI持续集成

    0.前言 前段时间,考虑到要练习部署一套CI/CD的系统.一开始考虑到Jenkins,随着这两天的了解,发现最新版的GitLab已经提供有CI/CD集成了.所以本次博客,干脆一步到位,直接用GitLa ...

  10. 【Luogu P2146】软件包管理器

    Luogu P2146 由于对于每一个软件包有且只有一个依赖的软件包,且依赖关系不存在环. 很显然这是一个树形的结构. 再看题目要求的操作,安装实际上对应的是覆盖根节点到当前节点的路径,卸载则是覆盖该 ...