代理与反射(一)

499 阅读4分钟

ECMAScript6新增的代理和反射为开发者提供了拦截并向基本操作嵌入额外行为的能力。具体来讲就是可以给目标对象定义一个关联的代理对象,而这个代理对象可以作为抽象的目标函数来使用。

1.代理基础

     代理是目标对象的抽象。从很多方面来看,代理类似C++指针,因为它可以用作目标对象的替身,但是又完全独立于目标对象,目标对象即可以直接被操作,也可以通过代理来操作。但是直接操作会绕过代理施予的行为。

a.创建proxy空代理

    代理是使用proxy构造函数创建的。该有两个参数:目标对象和处理程序对象,缺少任何一个参数都会抛出TypeError.要创建空代理,可以传入一个简单的对象字面量作为处理程序对象,而后所有的操作畅通无阻的抵达目标对象。

话不多说,上栗子^~^:

在代理对象上执行的任何操作实际上都会应用到目标对象,唯一可以感知的不同就是代码中操作的是代理对象。

const target = {
 id:'kkb'
};
const handler = {};
const proxy = new Proxy(target,handler);
// id属性会访问同一个值
console.log(target.id);  //kkb
console.log(proxy.id);   //kkb
 //给目标属性赋值会返回在两个函数上,因为两个对象访问的是同一个值
target.id = 'kaikeba';
console.log(target.id);  //kaikeba
console.log(proxy.id);   //kaikeba

//给代理属性赋值会反应在两个对象上,因为这个赋值会转移到目标对象
proxy.id = 'kaikebaTwo';
console.log(target.id);  //kaikebaTwo
console.log(proxy.id);   //kaikebaTwo
//hasOwnProperty()方法在两个地方,都会应用到目标对象;
console.log(target.hasOwnProperty('id'));        //true
console.log(proxy.hasOwnProperty('id'));         //true
Proxy.prototypeundefined,因此不能用instanceOf操作符
console.log(target instanceOf Proxy);  //TypeError : function has non-object prototype 'undefined' in instanceOf check
console.log(proxy instanceOf Proxy);  //TypeError : function has non-object prototype 'undefined' in instanceOf check

 b.定义捕获器

使用代理的主要目的是可以定义捕获器。每个处理程序对象可以包含零个或者多个捕获器,每个捕获器都对应一种基本操作,可以直接或者间接在代理对象上调用。

捕获器是程序流中的一个同步中断,可以暂停程序流,转而执行一段子例程,之后再返回原始程序流。

例子:可以定义一个get()捕获器,再ECMAScript操作以某种形式调用get()时触发

const kkb = {
    foo:"hello  word"
};
coonst handler = {
    //捕获器再处理程序对象中以方法名为键
    get(){
        return  'handler override';
    }
};
const proxy = new Proxy(kkb,handler);

这样当通过代理对象执行get()操作的时候,就会触发定义的get()捕获器,当然,get()不是ECMAScript对象可以调用的方法,这个方法在JavaScript代码中可以通过多种形式触发并被get()捕获器拦截到。proxy[property]、proxy.property或Object.creat(proxy)[property]等操作都会触发基本的get()操作以获取属性。注:只有在代理对象上执行这些操作才会触发捕获器。

console.log(kkb.foo)  //hello word
console.log(proxy.foo)  //handler override

c.捕获器的参数和反射API

基于相对应的参数可以重建被捕获方法的原始行为,拿上面的get()来讲,捕获器会接收到目标对象,要查的属性和代理对象的三个参数。

const kkb = {
    foo:"hello  word"
};
const handler = {
   
    get(trapTarget,property,receiver){
        console.log(trapTarget===kkb);    //true
	console.log(property);             //foo
	console.log(receiver===proxy);     //true
    }
};
const proxy = new Proxy(kkb,handler);
proxy.foo;

有了这些参数,可以重建被捕获方法的原始方法;

const kkb = {
    foo:"hello  word"
};
const handler = {   
    get(trapTarget,property,receiver){
       return   trapTarget[property]
    }
};
const proxy = new Proxy(kkb,handler);
console.log(proxy.foo);   //hello  wordconsole.log(kkb.foo);     //hello  word

  实际上,开发者可以通过全局Reflect对象上(封装了原始行为)的同名方法来轻松重建。

处理程序对象中所有可以捕获的方法都有对应的反射(Reflect)API方法。

const kkb = {
    foo:"hello  word"
};
const handler = {
    get(){
       return  Reflect.get(...arguments)
    }
};
//或者简写为 Reflect.get
const handler = {
      get:Reflect.get 
};const proxy = new Proxy(kkb,handler);
console.log(proxy.foo);  //hello  wordconsole.log(kkb.foo);    //hello  word

 事实上,如果想创建一个可以捕获所有方法,然后将每个方法转发给对应反射API的空代理,可以写成如下的方式

const kkb = {
    foo:"hello  word"
};
const proxy = new Proxy(kkb,Reflect);
console.log(proxy.foo);  //hello  word
console.log(kkb.foo);    //hello  word

d.代理另一个代理

代理可以拦截反射API的操作,而这就意味着完全可以创建一个代理,通过它去代理另一个代理,这样就可以在一个目标对象上构建多层拦截网。

const target = {
    foo:"hello  word"
};
const proxyOne = new Proxy(target,{
    get(){
        console.log('proxy   One')
        return Reflect.get(...arguments)
    }
});
const proxyTwo = new Proxy(proxyOne ,{    get(){
        console.log('proxy   Two')
        return Reflect.get(...arguments)
    }
});
console.log(proxyTwo.foo)//proxyTwo
//proxyOne 
//hello word