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还没有那么流行的时候(暴露年纪),我们去银行存款或者取款的时候,需要在柜台前排队,等柜台工作人员 ...
随机推荐
- 搞清楚 Python 的迭代器、可迭代对象、生成器
很多伙伴对 Python 的迭代器.可迭代对象.生成器这几个概念有点搞不清楚,我来说说我的理解,希望对需要的朋友有所帮助. 1 迭代器协议 迭代器协议是核心,搞懂了这个,上面的几个概念也就很好理解了. ...
- 安装Windows和Ubuntu双系统--Ubuntu安装过程识别不了硬盘
Linux识别不了固态硬盘 安装过程: 自己本身的是Windows 10,一块125g 固态 ,一块1T的机械硬盘. 通过rufus 制作ubuntu的启动盘 在BIOS中关闭电脑的安全启动选项,并且 ...
- python多线程总结
概述 进程与线程 进程:进程是资源(CPU.内存等)分配的最小单位,进程有独立的地址空间与系统资源,一个进程可以包含一个或多个线程 线程:线程是CPU调度的最小单位,是进程的一个执行流,线程依赖于进程 ...
- K8S入门系列之集群二进制部署-->node篇(三)
node节点组件 docker kubelet kube-proxy kubernetes-server-linux-amd64.tar.gz(相关的这里都能找到二进制文件!) falnnel 1. ...
- 必知必会的JavaJDK工具
JDK中有很多用于监控诊断的系统工具,对于Java程序员来说,无疑是用来了解自己程序运行时性能好坏的强大工具. 在JDK的bin目录下就可以找到这些工具. JPS 在Linux有一个命令叫做ps,可以 ...
- docker中部署项目时遇到的问题
容器和宿主机时间不同步问题? 将本地时间复制到docker容器内的etc文件夹下即可 docker cp /etc/localtime scrapy_8:/etc/ 启动crontab错误? 报错: ...
- dbstructsync 多套mysql环境表、字段、索引的差异sql产出(原创)
最近写了一个工具(比较两套测试环境数据库表.表字段.索引的差异) 功能:可以比较两套环境中mysql指定库中表.表字段及索引的差异,返回具体需要同步的执行sql A环境的数据库db 作为sourced ...
- thinking in JAVA 编译记录
编辑/编译<thinking in JAVA>源代码 一.下载源代码 首先,我阅读的是<thinking in JAVA>第四版,因此按照书中提供的链接找到了mindview主 ...
- 十、GAP
1.1 背景 GAP(Generic Access Profile)位于主机协议栈的最顶层,用来定义BLE设备在待机或者连接状态中的行为,该Profile保证不同的Bluetooth产品可以互 ...
- python容器类型集合的操作
集合(set):集合是一个无序的序列,集合中的元素可以是任意数据类型:表现形式是set(集合的元素),能够实现自动去重:集合传入的必须是一个hashable类型值,(不能存储字典格式的值):并且创建集 ...