es6 proxy浅析
Proxy
使用proxy,你可以把老虎伪装成猫的外表,这有几个例子,希望能让你感受到proxy的威力。
proxy 用来定义自定义的基本操作行为,比如查找、赋值、枚举性、函数调用等。
proxy接受一个待代理目标对象和一些包含元操作的对象,为待代理目标创建一个‘屏障’,并拦截所有操作,重定向到自定义的元操作对象上。
proxy通过new Proxy
来创建,接受两个参数:
- 待代理目标对象
- 元操作对象
闲话少说,直接看例子。
最简单的只代理一个方功能,在这个例子里,我们让get
操作,永远返回一个固定的值
let target = {
name: 'fox',
age: 23
}
let handler = {
get: (obj, k) => 233
}
target = new Proxy(target, handler);
target.a // 233
target.b // 233
target.c // 233
无论你taget.x
、target[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解决这个问题
const defaultValueObj = (target, defaultValue) => new Proxy(target, {
get: (obj, k) => Reflect.has(obj, k) ? obj[k] : defaultValue
})
建议根据不同类型返回不同的默认值,Number => 0 String => '' Object => {} Array => []等等
数组负索引取值
js中,获取数组的最后一个元素是相对麻烦的,容易出错的。这就是为什么TC39提案定义一个方便的属性,Array.lastItem
去获取最后一个元素。
其他语言比如python,和ruby提供了访问数组最后一个元素的方法,例如使用arr[-1]代替arr[arr.length - 1]
不过,我们有proxy,负索引在js中也可以实现。
const negativeArray = els => new Proxy(els, {
get: (target, k) => Reflect.get(target, +k < 0 ? String(target.length + +k) : k)
})
需要注意的一点是,get操作会字符串化所有的操作,所以我们需要转换成number在进行操作,
这个运用也是negative-array
的原理
隐藏属性
js未能实现私有属性,尽管之后引入了Symbol
去设置独一无二的属性,但是这个被后来的Object.getOwnPropertySumbols
淡化了
长期以来,人们使用下划线_来表示属性的私有,这意味着不运行外部操作该属性。不过,proxy提供了一种更好的方法来实现类似的私有属性
const enablePrivate = (target, prefix = '_') => new Proxy(target, {
has: (obj, k) => (!k.startsWith(prefix) && k in obj),
ownKeys: (obj, k) => Reflece.ownKeys(obj).filter(k => (typeof k !== 'string' || !k.startsWith(prefix))),
get: (obj, k, rec) => (k in rec) ? obj[k] : undefined
})
结果
let userData = enablePrivate({
firstName: 'Tom',
mediumHandle: '@tbarrasso',
_favoriteRapper: 'Drake'
})
userData._favoriteRapper // undefined
('_favoriteRapper' in userData) // false
Object.keys(userData) // ['firstName', 'mediumHandle']
如果你打印该proxy代理对象,会在控制台看到,不过无所谓。
缓存失效
服务端和客户端同步一个状态可能会出现问题,这很常见,在整个操作周期内,数据都有可能被改变,并且很难去掌握需要重新同步的时机。
proxy提供了一种新的办法,可以让属性在必要的时候失效,所有的访问操作,都会被检查判断,是否返回缓存还是进行其他行为的响应。
const timeExpired = (target, ttl = 60) => {
const created_at = Date.now();
const isExpired = () => (Date.now - created_at) > ttl * 1000;
return new Proxy(tarvet, {
get: (target, k) => isExpired() ? undefined : Reflect.get(target, k);
})
}
上面的功能很简单,他在一定时间内正常返回访问的属性,当超出ttl时间后,会返回undefined。
let timeExpired = ephemeral({
balance: 14.93
}, 10)
console.log(bankAccount.balance) // 14.93
setTimeout(() => {
console.log(bankAccount.balance) // undefined
}, 10 * 1000)
上面的例子会输出undefined在十秒后,更多的骚操作还请自行斟酌。
只读
尽管Object.freeze
可以让对象变得只读,但是我们可以提供更好的方法,让开发者在操作属性的时候获取明确的提示
const nope = () => {
throw new Error('不能改变只读属性')
}
const read_only = (obj) => new Proxy(obj, {
set: nope,
defineProperty: nope,
deleteProperty: nope,
preentExtensions: nope,
setPrototypeOf: nope
});
枚举
结合上面的只读方法
const createEnum = (target) => read_only(new Proxy(target, {
get: (obj, k) = {
if (k in obj) {
return Reflect.get(obj, k)
}
throw new ReferenceError(`找不到属性${k}`)
}
}))
我们得到了一个对象,如果你访问不存在的属性,不会得到undefined,而是抛出一个指向异常错误,折让调试变得更方便。
这也是一个代理代理的例子,需要保证被代理的代理是一个合法的代理对象,这个有助于混合一些复杂的功能。
重载操作符
最神奇的可能就是重载某些操作符了,比如使用handler.has
重载in
。
in用来判断指定的属性是否指定对象或者对象的原型链上,这种行为可以很优雅的被重载,比如创建一个用于判断目标数字是否在制定范围内的代理
const range = (min, max) => new Proxy(Object.create(null), {
has: (obj, k) => (+k > min && +k < max)
})
const X = 10.5
const nums = [1, 5, X, 50, 100]
if (X in range(1, 100)) { // true
// ...
}
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浅析的更多相关文章
- ES6 Proxy 性能之我见
ES6 Proxy 性能之我见 本文翻译自https://thecodebarbarian.com/thoughts-on-es6-proxies-performance Proxy是ES6的一个强力 ...
- Vue3.0 响应式数据原理:ES6 Proxy
Vue3.0 开始用 Proxy 代替 Object.defineProperty了,这篇文章结合实例教你如何使用Proxy 本篇文章同时收录[前端知识点]中,链接直达 阅读本文您将收获 JavaSc ...
- ES6 Proxy和Reflect(下)
construct() construct方法用于拦截new命令. var handler = { construct (target, args) { return new target(...ar ...
- ES6 Proxy和Reflect (上)
Proxy概述 Proxy用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种"元编程"(meta programming),即对编程语言进行编程. Proxy可以理 ...
- es6 proxy代理
es6 新增构造函数 Proxy Proxy 构造函数,可以使用new 去创建,可以往里面插入两个参数,都是对象 let target = {} let handler = {} let proxy ...
- es6 Proxy
proxy在语言层面去操作一个对象 var user={}; user.fname='Bob'; user.lname="Wood"; user.fullName= functio ...
- ES6 Proxy的应用场景
一.相关API Proxy Reflect 二.Proxy应用场景 1.数据校验 表单提交的时候做数据校验,例如年龄是不是满足条件,数据类型是不是满足要求等等,这场场景非常适合使用Proxy. 下面展 ...
- es6 Proxy对象详解
Proxy用于修改某些操作的默认行为,也可以理解为在目标对象之前架设一层拦截,外部所有的访问都必须先通过这层拦截,因此提供了一种机制,可以对外部的访问进行过滤和修改.这个词的原理为代理,在这里可以表示 ...
- 新的知识点来了-ES6 Proxy代理 和 去银行存款有什么关系?
ES给开发者提供了一个新特性:Proxy,就是代理的意思.也就是我们这一节要介绍的知识点. 以前,ATM还没有那么流行的时候(暴露年纪),我们去银行存款或者取款的时候,需要在柜台前排队,等柜台工作人员 ...
随机推荐
- Ubuntu编译安装HAprox+Keepalived+MySQL负载高可用架构(结合Docker容器配置)
系统环境:Ubuntu16.04(Docker容器) 架构环境: Keepalived/HAproxy MASTER: 172.17.0.4 Keepalived/HAproxy BACKUP: 17 ...
- Zabbix-(三)监控主机CPU、磁盘、内存并创建监控图形
Zabbix-(三)监控主机CPU.磁盘.内存并创建监控图形 一.前言 前文中已经讲述了两种方式对Zabbix的搭建,本文将讲述如何在zaibbx上添加需要监控的主机,以及使用Zabbix自带模板和自 ...
- 深入理解 PHP 的 7 个预定义接口
深入理解预定义接口 场景:平常工作中写的都是业务模块,很少会去实现这样的接口,但是在框架里面用的倒是很多. 1. Traversable(遍历)接口 该接口不能被类直接实现,如果直接写了一个普通类 ...
- PHP 当Swoole 遇上 ThinkPHP5
本文假设你已经有了 Linux 操作系统的 PHP 环境,强烈推荐使用 Vagrant 来搭建开发环境 安装 Swoole PECL 拓展可以通过 pecl 命令或者通过源码包编译安装,本文采用 pe ...
- nyoj 61-传纸条(一)(双向dp)
61-传纸条(一) 内存限制:64MB 时间限制:2000ms Special Judge: No accepted:8 submit:37 题目描述: 小渊和小轩是好朋友也是同班同学,他们在一起总有 ...
- .NET Core 3.0中用 Code-First 方式创建 gRPC 服务与客户端
.NET Core love gRPC 千呼万唤的 .NET Core 3.0 终于在 9 月份正式发布,在它的众多新特性中,除了性能得到了大大提高,比较受关注的应该是 ASP.NET Core 3. ...
- js数组合并以及对象的遍历
这是很基础的知识,but,对于一只未系统学习过js,只略懂搬砖的跨界狗,还是经常犯错: 场景:移动端上拉加载更多. 初始数组合并后来请求的数组. 使用concat方法,不过要主要: 使用concat, ...
- vim编辑中断后,重新编辑的警告删除
使用vim 编辑,遇到突然中断,比如ssh远程时断网了. 如果再次ssh连接,重新vim 打开之前在编辑的文件,会有类似如下的警告 这是因为vim会在被编辑的目录下新建一个名为.filename.sw ...
- 物联网架构成长之路(47)-利用GitLab实现CI持续集成
0.前言 前段时间,考虑到要练习部署一套CI/CD的系统.一开始考虑到Jenkins,随着这两天的了解,发现最新版的GitLab已经提供有CI/CD集成了.所以本次博客,干脆一步到位,直接用GitLa ...
- 【Luogu P2146】软件包管理器
Luogu P2146 由于对于每一个软件包有且只有一个依赖的软件包,且依赖关系不存在环. 很显然这是一个树形的结构. 再看题目要求的操作,安装实际上对应的是覆盖根节点到当前节点的路径,卸载则是覆盖该 ...