最简单的代理是空代理,就是说除了做为一个抽象的目标对象,什么也不作,在代理对象是执行的全部操做都会无障碍的传播到目标对象,所以,任何使用目标对象的地方,均可以经过一样的方式使用与之关联的代理对象markdown
例子1:函数
const target = { id: 'target' };
const handler = {}; const proxy = new Proxy(target, handler); // id属性会访问同一个值 console.log(target.id); // target console.log(proxy.id); // target // 给目标属性赋值会反映在两个对象上, 由于两个对象访问的是同一个值 target.id = 'foo'; console.log(target.id); // foo console.log(proxy.id); // foo // 给代理属性赋值会反映在两个对象上,由于这个赋值会转移到目标对象 proxy.id = 'bar'; console.log(target.id); // bar console.log(proxy.id); // bar // hasOwnProperty()方法在两个地方 都会应用到目标对象 console.log(target.hasOwnProperty('id')); // true console.log(proxy.hasOwnProperty('id')); // true 复制代码
小结:这是最简单的一个空代理的案例,初看可能会不明因此,我当初看也很懵,可是不要着急,你如今只须要看懂,知道代理作了什么事情,在这个例子里,target是代理的目标,proxy是代理,代理的目标和代理属性共享,操做共享,包括方法也是共享的,可是代理并不等于代理目标,更倾向于操做代理时,代理把操做转发给了代理的目标,而后进行操做,目标拥有的,代理会拥有,代理拥有的,也会相应的传递给目标;oop
使用代理的「目的」 :是能够定义「捕获器」(trap),捕获器就是能够直接或间接在代理对象上调用,每次在代理对象上调用这些基本操做时, 代理能够在这些操做传播到目标对象以前先调用捕获器函数,从而拦 截并修改相应的行为。ui
「handler」 :代理的处理对象,例子1中是一个空对象,多数状况下并非空对象,而是定义了一个或者多个捕获器(trap)去处理代理,若是没有定义,则和上例中的空对象同样,使用默认行为。this
「set trap」 :经常使用的trap,触发条件是在设置属性值的时候触发,spa
「Reflect.set」: 将值分配给属性的函数。返回一个Boolean
,若是更新成功,则返回true
代理
首先set trap接受4个参数,code
trapTarget - 接收的属性的对象,就是**代理的目标 **orm
key - 要写入的属性的「键」对象
value- 写入属性的「值」
receiver- 操做的对象,一般是「代理」
例子2:用set trap验证一个属性的值是否为number
let target = {
name: "target" }; let proxy = new Proxy(target, { //target,name,target,proxy; set(trapTarget, key, value, receiver) { console.log(`trapTarget is ${trapTarget}, key is ${key}, value is ${value}, receiver is ${receiver}`) // 忽视存在的属性,以避免产生影响,不存在的属性才会进入判断 if (!trapTarget.hasOwnProperty(key)) { //判断值是否为number if (isNaN(value)) { throw new TypeError("Property must be a number."); } } // 添加到属性 return Reflect.set(trapTarget, key, value, receiver); } }); // 添加一个新的属性 proxy.count = 1; console.log(proxy.count); // 1 console.log(target.count); // 1 // 赋值给存在target上的属性 proxy.name = "proxy"; console.log(proxy.name); // "proxy" console.log(target.name); // "proxy" // 新的属性值不是数字会抛出异常 proxy.anotherName = "proxy"; 复制代码
小结:这个例子打印出了4个参数的时刻变化,能够清晰的看到参数对应的值,能够发现,每次设置属性值的时候都会进行拦截判断,因此在获取时候,能够用get进行拦截判断,
复制代码
「get trap」:
get 是读取对象属性的时候用到的trap,他接受3个参数
trapTarget: 从哪一个对象读取的属性,就是target
key
- 读取的key
receiver
- 操做的对象,一般是代理(proxy)
例子1:这是一个普通的get代理的按理
const handler = {
get: function(obj, prop) { return prop in obj ? obj[prop] : 37; } }; const p = new Proxy({}, handler); p.a = 1; p.b = undefined; console.log(p.a, p.b); // 1, undefined console.log('c' in p, p.c); // false, 37 复制代码
小结:p.a的时候,获取代理里面a的值通过get,返回了 a的值,p.b同样,而后c in p,由于p没有c这个属性,因此会返回false,p.c像p获取c属性,进入get,赋值为37,
复制代码
再来看一个set 和get的综合例子
例子3;
var p = {
name:"chen", work:function() { console.log("wording..."); }, _age:18, //监听获取name属性的事件 get name(){ return this._age; }, //监听设置age属性的事件 set age(val) { if (val<0 || val> 100) {//若是年龄大于100就抛出错误 throw new Error("invalid value") }else{ this._age = val; } } }; console.log(p.name);//输出18,由于这里触发了监听name属性的拦截,因此返回的age, console.log(p._age);//这里的age没有获取拦截,因此输出原值 console.log(p._age =20);//输出20,由于这里进入设置拦截,知足条件,完成赋值 console.log(p._age =200);//报错,设置拦截进入,不知足条件,报错 console.log(p.name = 'yu')//输出 yu,由于name没有设置拦截,因此能够成功 复制代码
小结:看完这个,代理的基本知识及其原理相信你们都明白了,其实捕获器还有不少,列如has等等,可是基本原理都是这样,只要把握住参数分别表明什么,捕获器的触发条件,问题均可以迎刃而解了,但愿能够给你们带来帮助
本文使用 mdnice 排版