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还没有那么流行的时候(暴露年纪),我们去银行存款或者取款的时候,需要在柜台前排队,等柜台工作人员 ...
随机推荐
- csps模拟测试57
T1 天空龙 大神题,考察多方面知识,例如:快读 附上考试代码,以供后人学习 应某迪要求,我决定多写一点. 正如文化课有知识性失分和非知识性失分一样,OI也同样存在. 但非知识性失分往往比知识性失分更 ...
- SpringBoot项目热启动
一.添加POM依赖 <!-- 热部署模块 --> <dependency> <groupId>org.springframework.boot</groupI ...
- SpringBoot Web篇笔记(一)
摘要 文章是根据江南一点雨(松哥)的视频进行总结 江南一点雨博客 全局异常处理 通常情况下,我们都需要对自己定义的异常进行相应的处理.捕获指定的异常方式如下: @ControllerAdvice pu ...
- 2.基础:Vue组件的核心概念
一.组件基础和注册 组件概念 组件系统是 Vue 的另一个重要概念,他的核心就是封装和复用. 细节 组件的name必须是全局唯一. 二.属性.事件和插槽 组件的三大核心概念:属性.事件和插槽. 属性, ...
- canvas与工作流的不解之缘
html的标签 <canvas>用于图形的绘制,通过脚本 (通常是JavaScript)来完成,canvas简而言之就是个画布.上一篇文章我们提到工作流的一个重要组成部分:流程建模,也就是 ...
- python基础-面向过程编程
面向过程编程 面向过程编程其实是一种机械式的思维方式,其核心就是"过程". 过程指的是一种解决问题的步骤,即先干什么再干什么,最后干什么. 优点:将复杂的问题流程化,进而简单化. ...
- Geotools求shapefile路网中任意两点之间最短路径的距离
前言:之前在博问求助过这个问题.经过几天的思考,算是解决了(但仍有不足),另一方面对Geotools不是很熟,有些描述可能不正确,希望大家批评指正. 问题:作为一个新手,我并没有发现Geotools中 ...
- 过滤广告(只能发布 [a-zA-z0-9及汉字,;?.]) ,排除其他特殊符号
/** * 过滤广告(只能发布 [a-zA-z0-9及汉字,;?.]) ,排除其他特殊符号 * Created by 1 on 2015/8/19. */ public class FilterA ...
- 快速搭建Jenkins集群
关于Jenkins集群 在Jenkins上同时执行多个任务时,单机性能可能达到瓶颈,使用Jenkins集群可以有效的解决此问题,让多台机器同时处理这些任务可以将压力分散,对单机版Jenkins的单点故 ...
- 创建基于OData的Web API - Knowledge Builder API, Part III:Write Model
在前两篇文章<Part I: Business Scenario> 和<Part II: Project Setup>后,可以开始真正Model的创建. 步骤如下: 1. 创建 ...