博客
电影
宝箱
友链
关于
<
但丁《神曲》炼狱篇,赎罪与升华
轻应用PWA实践全过程
>
JS代理Proxy与反射Reflect场景
作者:
Cifer
类别: 技术·JS
时间:2019-03-20 01:09:22
字数:4767
版权所有,未经允许,请勿转载,谢谢合作~
###概念 ES6增加的特性中,其中包含代理(Proxy)与反射(Reflection)。 代理Proxy是一种可以拦截及改变JS引擎操作的包装器,相当于改变成JS语言的对象中的默认行为,使得开发人员能够实现对JS更底层的操作。 反射Reflect是一种与Object类似的实现,只不过它包含的是语言内部的方法,旧方法(如defineProperty)会在Object/Reflect同存在,语言性的新方法只会存在于Reflect。 Proxy拦截默认行为方法的方法,我们称之为代理陷阱,每个代理陷阱都会有一个与之对应的Reflect方法,Proxy与Reflect虽然是互相独立的,配合起来却是所向披靡。 ### Proxy与Reflect的对应表 为了更好理解,我们先引入一个最简单的例子,实现当试图从一个对象取它不存在的属性,抛出错误: ```javascript const obj = {}; const pObj = new Proxy(obj, { get(target, key, receiver) { if (!(key in receiver)) { throw new TypeError(`Property ${key} not exist!`); } return Reflect.get(target, key, receiver); } }); obj.name = 'boatsky.com'; console.log(obj.name); // boatsky.com console.log(pObj.name); // boatsky.com console.log(obj.id); // undefined console.log(pObj.id); // Uncaught TypeError: Property id not exist! ``` target, key, receiver 三者为代理陷阱的参数,分别表示被代理的目标对象,键,代理对象。虽然obj与pObj一样的,但我们只对pObj进行修改,从而更改obj。 代理中的get与Reflect.get()有着对应关系,举一反三,我们例出13个陷阱与Reflect方法: | 代理陷阱 | 触发 | 对应默认方法 | 参数 | | ------------ | ------------ | ------------ | ------------ | | get | 读取属性值 | Reflect.get() | target,key,receiver | | set | 写入属性 | Reflect.set() | target,key,value,receiver | | has | in操作 | Reflect.has() | target,key | | getPrototypeof | Object.getPrototypeof() | Reflect.getPrototypeof() | target | | setPrototypeof | Object.setPrototypeof() | Reflect.setPrototypeof() | proto | | defineProperty | Object.defineProperty() | Reflect.defineProperty() | target,key,descriptor | | deleteProperty | Object.deleteProperty() | Reflect.deleteProperty() | target,key | | getOwnPropertyDescriptor | Object.getOwnPropertyDescriptor() | Reflect.getOwnPropertyDescriptor() | targe,key | | apply | 调用一个函数 | Reflect.apply() | target, thisArg,argumentList | | construct | new调用一个函数 | Reflect.construct() | ...values | | isExtensible | Object.isExtensible() | Reflect.isExtensible() | target | | preventExtensions | Object.preventExtensions() | Reflect.preventExtensions() | target | | ownKeys | Object.keys(),<br>Object.getOwnPropertyNames(),<br>Object.getOwnPropertySymbols() | Reflect.ownKeys() | target | 根据各个代理陷阱的作用,可以做不同的事情,这里就不一一列举定义的例子了,而是举例一些更业务型的例子。 ### 给数组增加查询方法 比如有一个数组,元素都是对象,需要使用getItemById,getItemByName,getItemByAge……等等任意getItemByXx的方法查询出数据。 ```javascript const arr = [ { id: 1, name: 'seven', age: 17, }, { id: 2, name: 'dong', age: 16, }, { id: 3, name: 'cifer', age: 17, }, ]; function proxyArr(arr) { const prefix = 'getItemBy'; return new Proxy(arr, { get: function (target, key) { if (key in target) { return target[key]; } if (key.startsWith(prefix)) { const realKey = key.substr(prefix.length).toLowerCase(); return function(v) { const result = target.filter(i => i[realKey] === v); return result.length >= 2 ? result : result[0]; } } } }); } let pArr = proxyArr(arr); console.log(pArr == arr); console.log(pArr.getItemById(3)); // {id: 3, name: "cifer", age: 17} console.log(pArr.getItemByName('dong')); // {id: 2, name: "dong", age: 16} console.log(pArr.getItemByAge(17)); // [{id: 1, name: "seven", age: 17}, {id: 3, name: "cifer", age: 17}] ``` ### 监听多重等号执行过程 我们知道 a = b = x的过程是从右到左 b = x, a = x,却难以用代码来证明这个过程(比如从表面上看,也可能是 b = x, a = b),而Proxy提供了这种可能 ```javascript const obj = {}; const pObj = new Proxy(obj, { get: function (target, key, receiver) { console.log(`get ${key}!`); return Reflect.get(target, key, receiver); }, set: function (target, key, value, receiver) { console.log(`set ${key}!`); return Reflect.set(target, key, value, receiver); } }); pObj.a = {name: 'a'};// set a; pObj.b = pObj.a; // get a; set b; pObj.a.m = pObj.a = {name: 'b'}; // get a; set a; console.log(obj); // {a: {name: 'b'}, b: {name: 'a', m: {name: 'b'}}} ``` pObj.a.m = pObj.a = {name: 'b'}; 先取出pObj.a 再设置,而pObj.a.m未修改pObj的直接属性,不会触发。最终obj打印的结果,可以看出,pObj.a.m拿的是pObj.b。 换种写法 ```javascript pObj.a = {name: 'a', id: 1};//set a! pObj.b = pObj.a.m = {name: 'b'};// get a! set b! console.log(obj); // {a: {name: 'a', id: 1, m: {name: 'b'}}, b: {name: 'b'}} ``` 说明过程是b = x;a =x;而不是b = x; a = b; ### 代理Proxy实现节流/防抖 ```javascript function workSomething (i) { console.log(new Date().getTime(), i); // 最终打印 1553181456163 4 } function makeThrottle(fn, delay) { let makeTimer = null; return new Proxy(fn, { apply (target, thisArg, argumentList) { clearTimeout(makeTimer); makeTimer = setTimeout(() => { Reflect.apply(target, thisArg, argumentList); }, delay); } }); } const workSomethingProxy = makeThrottle(workSomething, 100); for (let i = 0; i < 5; i++) { workSomethingProxy(i); } ``` 因为调用函数时,会触发apply代理陷阱,我们可以在里面阻止事件执行,使得delay满足,这与传统的节流/防抖实现差不多是一个意思,Proxy并没有更高明,只不过是换一种方式。 Proxy与Reflect的实例数不胜数,点到为止了,以后看到有趣的实例再补充。 参考资料: 《深入理解ES6》 《ECMAScript 6入门》 <a href="https://jsonz1993.github.io/2018/07/circular-reference/" target="_blank" rel="nofollow">一道赋值面试题引发的思考</a>
如果觉得有帮忙,您可以在本页底部留言。
相关推荐:
NPM发包流程与技巧
Puppeteer爬取豆瓣电影TOP250评分
基于vue实现腾讯云点播的上传与播放
轻应用PWA实践全过程
ES6迭代器Iterator原理与性能
JavaScript之Set与Map
ES6设计模式之观察者模式
解决toFixed四舍五入陷阱
深入理解IEEE754的64位双精度
ES6设计模式之单例模式
ES6设计模式之工厂模式
ES6二叉树的实现
JavaScript链表实例
JavaScript排序算法及性能比较
原生ajax及jQuery封装ajax实例
JavaScript类的语法糖
……
更多
<
但丁《神曲》炼狱篇,赎罪与升华
轻应用PWA实践全过程
>
全部留言
我要留言
内容:
网名:
邮箱:
个人网站:
发表
全部留言