ES6之Proxy
来源:互联网 发布:卷积神经网络算法原理 编辑:程序博客网 时间:2024/05/21 08:40
Proxy
ES6 中新增的 Proxy 特性为开发者提供了一种实现元编程的“大杀器”。
元编程概念可以简单地描述为:一个程序可以对另外一个程序进行读取、转换,甚至在这第二个程序运行的时候对其进行修改。
Proxy 让我们可以对任何对象的绝大部分行为进行监听和干涉,实现更多的自定义程序行为。
目录:
- Proxy
- 使用语法
- has
- get
- set
- apply
- construct
- 创建可解除 Proxy 对象
- 使用场景
- 看似不可能的自动填充
- 只读试图
- 入侵式测试框架
使用语法
用法:new Proxy(target, handler)
。
与 Getter/Setter 不同的是,Proxy 并不是以语法的形式使用,Proxy 通过设置行为监听方法来捕获程序对对应对象的行为。
const obj = {};const proxy = new Proxy(obj, { // ...})
Proxy 的构造器接受两个参数,第一个参数为需要进行包装的目标对象,第二个参数则为用于监听目标对象行为的监听器,其中监听器可以接受一些参数以监听相对应的程序行为。
has
可以通过为 Proxy 的 handler 定义 has 监听方法,来监听程序通过 in 语句来检查一个字符串或数字是否为该 Proxy 的目标对象中某个属性的属性键的过程。
const p = new Proxy({}, { has(target, prop){ console.log(`Checking "${prop}" is in the target or not`); return true; }})console.log('foo' in p);// Checking "foo" is in the target or not// true
该监听方法有两个需要注意的地方,如果遇到这两种情况,便会抛出 TypError 错误。
1.当目标对象被其他程序通过 Object.preventExtensions() 禁用了属性拓展(该对象无法再增加新的属性,只能对当前已有的属性进行操作,包括读取、操作和删除,但是一旦删除就无法再定义)功能,且被检查的属性键确实存在与目标对象中,该监听方法便不能返回 false。
const obj = {foo: 1};Object.preventExtensions(obj);const p = new Proxy(obj, { has(target, prop){ console.log(`Checking "${prop}" is in the target or not`); return false; }})console.log('foo' in p); // TypeError: 'has' on proxy: trap returned falsish for property 'foo' but the proxy target is not extensible
2.当被检查的属性键存在与目标对象中,且该属性的 configurable 配置是 false 时,该监听方法不能返回 false。
const obj = {};Object.defineProperty(obj, 'foo', { configurable: false, value: 10})const p = new Proxy(obj, { has(target, prop){ console.log(`Checking "${prop}" is in the target or not`); return false; }})console.log('foo' in p);// TypeError: 'has' on proxy: trap returned falsish for property 'foo' which exists in the proxy target as non-configurable
get
Getter只能对已知的属性键进行监听,而无法对所有属性读取行为进行拦截,而 Proxy 可以通过设定 get 监听方法,拦截和干涉目标对象的所有属性读取行为。
const obj = {foo: 1};const p = new Proxy(obj, { get(target, prop){ console.log(`Program is trying to fetch the property "${prop}".`); return target[prop]; }})p.foo; // Program is trying to fetch the property "foo".p.something; // Program is trying to fetch the property "something".
这个监听方法也存在需要注意的地方——当目标对象被读取属性的 configurable 和 writable 属性都为 false 时,监听方法最后返回的值必须与目标对象的原属性值一致。
const obj = {};Object.defineProperty(obj, 'foo', { configurable: false, enumberable: false, value: 10, writable: false})const p = new Proxy(obj, { get(target, prop){ return 20; }})console.log(p.foo);// TypeError: 'get' on proxy: property 'foo' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value (expected '10' but got '20')
set
handler.set 用于监听目标对象的所有属性赋值行为。
const obj = {};const p = new Proxy(obj, { set(target, prop, value){ console.log(`Setting value "${value}" on the key "${prop}" in the target object`); target[prop] = value; return true; }})p.foo = 1; // Setting value "1" on the key "foo" in the target object
注意,如果目标对象自身的某个属性是不可写也不可配置的,那么 set 不得改变这个属性的值,只能返回同样的值,否则报错。
apply
handler.apply , Proxy 也为作为目标对象的函数提供了监听其调用行为的属性。
const sum = function(...args){ return args .map(Number) .filter(Boolean) .reduce((a, b) => a + b);}const p = new Proxy(sum, { apply(target, thisArg, args){ console.log(`Function is being called with arguments [${args.join()}] and context ${thisArg}`); return target.call(thisArg, ...args); }})console.log(p(1, 2, 3));// Function is being called with arguments [1,2,3] and context undefined// 6
construct
handler.construct, Proxy 也可以将类作为目标监听对象,并监听其通过 new 语句来生产新实例的行为,这同样可以使用再作为构造器的构造函数上。
class Foo{};const p = new Proxy(Foo, { construct(target, args, newTarget){ return {arguments: args} // 这里返回的结果会是 new 所得到的实例}})const obj = new p(1, 2, 3);console.log(obj.arguments); // [1, 2, 3]
创建可解除 Proxy 对象
用法:Proxy.revocable(target, handler) : (proxy, revoke)
。
const obj = {foo: 10};const revocable = Proxy.revocable(obj, { get(target, prop){ return 20; }})const proxy = revocable.proxy;console.log(proxy.foo); // 20revocable.revoke();console.log(proxy.foo); // TypeError: Cannot perform 'get' on a proxy that has been revoked
Proxy.revocable(target, handler) 会返回一个带有两个属性的对象,其中一个 proxy 便是该函数所生成的可解除 Proxy 对象,而另一个 revoke 则是将刚才的 Proxy 对象解除的解除方法。
使用场景
看似“不可能”的自动填充
class Tree{ constructor(){ return new Proxy({}, { get(target, prop){ if(!(prop in target)) target[prop] = new Tree(); return target[prop]; } }) }}const tree = new Tree();tree.brance1.brance2.leaf = 1;tree.brance1.brance2.brance3.leaf = 2;
只读试图
通过 Proxy 来对渲染数据对象的行为进行监听和干涉,对所有可能对渲染数据做出修改的行为进行拦截。
const NOPE = () => { throw new TypeError('Cannot modify the readonly data.');}function readonly(data){ return new Proxy(data, { set: NOPE, deleteProperty: NOPE, setPrototypeOf: NOPE, preventExtensions: NOPE, defineProperty: NOPE })}const data = {foo: 10};const readonlyData = readonly(data);readonlyData.foo = 2; // TypeError: Cannot modify the readonly data.
上面的方法并没有对深层结果进行修改拦截,即 readonlyData.foo.bar = 2; 是不会被拦截的,而且对表层数据的 get 行为也没有拦截。
我们可以对其进行修改,使其实现真正的“只读视图”:
function readonly(data){ return new Proxy(data, { get(target, prop){ const result = target[prop]; // 判断是否为引用类型 if(Object(result) === result){ return readonly(result); } return result; }, // ... })}const data = {foo: {bar: 1}};const readonlyData = readonly(data);readonlyData.foo.bar = 2; //TypeError: Cannot modify the readonly data.
入侵式测试框架
开发者可以通过使用 Proxy 在定义方法和逻辑代码之间建立一个隔离层,这样便可以在两个地方都无须做大量修改的情况下,实现一下非常深入的测试,从而开发出一个针对业务的入侵式测试框架。
我们通过 Proxy 对目标代码进行包装,比如我们对一些目标 API 方法(函数)打一个“钩子”(Hook),通过一些包装,可以对一些方法进行计时,此处使用 console.time 来进行简单的计时。
import api from './api';export default hook(api, { methodName(fn){ return funciton(..args){ console.time('methodName'); return fn(...arga) .then((...args) => { console.timeEnd('methodName'); return Promise.resolve(...args); }) } }})
为了实现这样的“钩子”机制,我们可以对目标对象的 get 行为进行监听和干涉,一旦发现属性键所对应的属性值为一个函数(方法),且在测试案例中存在相应的干涉机制,便将其传递到测试案例中。
function hook(obj, cases){ return new Proxy(obj, { get(target, prop){ if((prop in cases) && (typeof target[prop] === 'function')){ const fn = target[prop]; return cases[prop](fn); } return target[prop]; } })}
- ES6之Proxy
- ES6 proxy
- 【ES6】Proxy
- ES6 Proxy
- es6-Proxy
- es6-Proxy
- ES6-proxy
- es6 Proxy
- 【ES6】Proxy
- 初步探究ES6之Proxy代理
- 学习笔记:ES6之Proxy和Reflect
- 初步探究ES6之Proxy代理
- ES6学习之路(六) Proxy 代理器&Reflect
- ES6--Proxy和Reflect
- ES6 Proxy/Reflect 浅析
- ES6 -- 最佳代理:Proxy
- ES6 -- Proxy搭档:Reflect
- ES6总结--Proxy、Reflect
- 使用yum来下载RPM包而不进行安装
- 10.3在表尾插入生成单链表
- Trie 树实现《圣经》词频统计
- 习题6.10
- 240. Search a 2D Matrix II
- ES6之Proxy
- 欢迎使用CSDN-markdown编辑器
- Linux基础命令(二)之du、df详解
- 经验总结-完整介绍Android Studio中Git的使用之GitHub更新代码到本地(四)
- hisi平台遥控器按键适配
- 神经网络与机器学习(第3版)阅读笔记{第1章}
- shell、gawk、sed使用散记
- 设计模式之简单工厂模式
- leetcode算法课程第十周博客