代理就是一种由你创建的特殊对象,它“封装”另一个普通对象,或者说挡在这个普通对象前面,你可以在代理对象上注册特殊的处理函数(trap),代理上执行各种操作的时候会调用这个程序
1 2 3 4 5 6 7 8 9 10 11 12 var  obj={a :1 },    handles={         get (target,key,context ) {                          console .log("accessing" ,key);             return  Reflect .get(target,key,context)         }     } pobj=new  Proxy (obj,handlers); obj.a; pobj.a; 
 
这里的映射是有意对称的,每个代理处理函数在对应的元编程任务执行的时候进行拦截,而每个Reflefct工具在一个对象上执行相应的元编程任务,每个代理函数都有一个自动调用相应的Reflect工具的默认定义。
一、介绍 定义:  用于定义基本操作的自定义行为
本质:  修改的是程序默认形为,就形同于在编程语言层面上做修改,属于元编程(meta programming)
元编程(Metaprogramming,又译超编程 ,是指某类计算机程序的编写,这类计算机程序编写或者操纵其它程序(或者自身)作为它们的数据,或者在运行时完成部分本应在编译时完成的工作
一段代码来理解
1 2 3 4 5 6 7 1 #!/bin/ bash2 # metaprogram3echo '#!/bin/bash'  >program 4for  ((I=1 ; I<=1024 ; I++)) do  5     echo "echo $I"  >>program6done 7chmod +x program 
 
这段程序每执行一次能帮我们生成一个名为program的文件,文件内容为1024行echo,如果我们手动来写1024行代码,效率显然低效
元编程优点:与手工编写全部代码相比,程序员可以获得更高的工作效率 ,或者给与程序更大的灵活度去处理新的情形而无需重新编译 
 
Proxy 亦是如此,用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)
二、用法 Proxy为 构造函数,用来生成 Proxy 实例
1 1var proxy = new Proxy(target, handler) 
 
参数 target表示所要拦截的目标对象(任何类型的对象,包括原生数组,函数,甚至另一个代理))
handler通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为
handler解析 关于handler拦截属性,有如下:
get(target,propKey,receiver):拦截对象属性的读取 
set(target,propKey,value,receiver):拦截对象属性的设置 
has(target,propKey):拦截propKey in proxy的操作,返回一个布尔值 
deleteProperty(target,propKey):拦截delete proxy[propKey]的操作,返回一个布尔值 
ownKeys(target):拦截Object.keys(proxy)、for...in等循环,返回一个数组 
getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象 
defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc),返回一个布尔值 
preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值 
getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象 
isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值 
setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值 
apply(target, object, args):拦截 Proxy 实例作为函数调用的操作 
construct(target, args):拦截 Proxy 实例作为构造函数调用的操作 
 
Reflect 若需要在Proxy内部调用对象的默认行为,建议使用Reflect,其是ES6中操作对象而提供的新 API
基本特点:
只要Proxy对象具有的代理方法,Reflect对象全部具有,以静态方法的形式存在 
修改某些Object方法的返回结果,让其变得更合理(定义不存在属性行为的时候不报错而是返回false) 
让Object操作都变成函数行为 
 
下面我们介绍proxy几种用法:
get() get接受三个参数,依次为目标对象、属性名和 proxy 实例本身,最后一个参数可选
1 2 3 4 5 6 7 8 9 10 11 1var  person = { 2   name: "张三" 3 };4 5var  proxy = new  Proxy (person, { 6   get: function (target, propKey )  {7     return  Reflect .get(target,propKey)8   }9 });10 11proxy.name  
 
get能够对数组增删改查进行拦截,下面是试下你数组读取负数的索引
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 1function  createArray (...elements )  { 2   let  handler = {3     get (target, propKey, receiver )  {4       let  index = Number (propKey);5       if  (index < 0 ) {6         propKey = String (target.length + index);7       }8       return  Reflect .get(target, propKey, receiver);9     }10   };11 12   let  target = [];13   target.push(...elements);14   return  new  Proxy (target, handler);15 }16 17let  arr = createArray('a' , 'b' , 'c' ); 18arr[-1 ]  
 
注意:如果一个属性不可配置(configurable)且不可写(writable),则 Proxy 不能修改该属性,否则会报错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 1const  target = Object .defineProperties({}, { 2   foo: {3     value: 123 ,4     writable: false ,5     configurable: false 6   },7 });8 9const  handler = { 10   get (target, propKey )  {11     return  'abc' ;12   }13 };14 15const  proxy = new  Proxy (target, handler); 16 17proxy.foo 18 
 
set() set方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身
假定Person对象有一个age属性,该属性应该是一个不大于 200 的整数,那么可以使用Proxy保证age的属性值符合要求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 1let  validator = { 2   set: function (obj, prop, value )  {3     if  (prop === 'age' ) {4       if  (!Number .isInteger(value)) {5         throw  new  TypeError ('The age is not an integer' );6       }7       if  (value > 200 ) {8         throw  new  RangeError ('The age seems invalid' );9       }10     }11 12     13     obj[prop] = value;14   }15 };16 17let  person = new  Proxy ({}, validator); 18 19person.age = 100 ; 20 21person.age  22person.age = 'young'   23person.age = 300   
 
如果目标对象自身的某个属性,不可写且不可配置,那么set方法将不起作用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 1const  obj = {}; 2Object .defineProperty(obj, 'foo' , { 3   value: 'bar' ,4   writable: false ,5 });6 7const  handler = { 8   set: function (obj, prop, value, receiver )  {9     obj[prop] = 'baz' ;10   }11 };12 13const  proxy = new  Proxy (obj, handler); 14proxy.foo = 'baz' ; 15proxy.foo  
 
注意,严格模式下,set代理如果没有返回true,就会报错
1 2 3 4 5 6 7 8 9 10 11 1 'use strict' ;2const  handler = { 3   set: function (obj, prop, value, receiver )  {4     obj[prop] = receiver;5     6     return  false ;7   }8 };9const  proxy = new  Proxy ({}, handler); 10proxy.foo = 'bar' ; 11 
 
deleteProperty() deleteProperty方法用于拦截delete操作,如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 1var  handler = { 2   deleteProperty (target, key) {3     invariant(key, 'delete' );4     Reflect .deleteProperty(target,key)5     return  true ;6   }7 };8function  invariant (key, action) { 9   if  (key[0 ] === '_' ) {10     throw  new  Error (`无法删除私有属性` );11   }12 }13 14var  target = { _prop : 'foo'  }; 15var  proxy = new  Proxy (target, handler); 16delete  proxy._prop 17 
 
注意,目标对象自身的不可配置(configurable)的属性,不能被deleteProperty方法删除,否则报错
Reflect.apply() 静态方法 **Reflect**.apply()  通过指定的参数列表发起对目标(target)函数的调用。
1 Reflect.apply(target, thisArgument, argumentsList) 
 
参数 
返回值 返回值是调用完带着指定参数和 this 值的给定的函数后返回的结果。
可取消代理 普通代理总是陷入到目标对象,并且在创建之后不能修改——只要还保持着对这个代理的引用,代理的机制就将维持下去,如果你想要的创建一个在你想要停止它作为代理时便可以被停止的代理,可以创建可取消代理。(revocable proxy)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 var  obj={a :1 },handlers={     get (target,key,context ) {         console .log('accessing:' ,key);         return  target[key];     } }; var  p=Proxy .revocable(obj,handlers);const  pobj=p.proxy;const  prevoke=p.revoke;console .log(pobj.a);prevoke(); pobj.a() 
 
可取消代理可能的应用场景:在应用中把代理分发到第三方,其中管理你的模型数据,而不是给出真实模型本身的引用。如果你的模型对象改变或被替换,就可以使分发出去的代理失效,这样第三方就可以知晓变化并请求更新到这个模型的引用。
使用代理: 代理在前: 代理与目标交流,首先与代理交互,通过与代理交互来增加某些特殊的规则,这些是message本身没有的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 var  messages=[],handlers={     get (target,key ) {         if (typeof  target[key]=='string' ){             return  target[key].replace(/[^w]/g ,"" );         }         return  target[key];     },     set (target,key,val ) {                  if (typeof  val=='string' ){             if (target.indexOf(val)==-1 ){                 target.push(val.toLowerCase());             }         }         return  true ;     } } var  messages_proxy=new  Proxy (messages,handlers);messages_proxy.push('heLLo...' ,42 ,"wOrlD!!" ,"WoRld!!" ); messages_proxy.forEach(val => {     console .log(val); }) messages.forEach(v =>console .log(v)); 
 
代理在后: 让目标与代理交流,而不是代理与目标交流。代理只能与主对象交互,实现方式就是把proxy对象放到主对象的[[Prototype]]链中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var  handlers={    get (target,key,context ) {         return  function ( ) {             context.speak(key+"!" )         }     } }, catchall=new  Proxy ({},handlers), greeter={     speak (who="someone"  ) {         console .log("hello" ,who)     } } Object .setPrototypeOf(greeter,catchall);greeter.speak(); greeter.speak("world" ); greeter.everyone(); 
 
访问不存在的属性名抛出错误 代理在前 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 var  obj={    a :1 ,     foo ( ) {         console .log("a:" ,this .a)     } }, handlers={     get (target,key,context ) {         if (Reflect .has(target,key)){             return  Reflect .get(target,key,context)         }         else {             throw  Error ("no such property/method" )         }     },     set (target,key,val,context ) {         if (Reflect .has(target,key)){             return  Reflect .set(target,key,val,context)         }else {             throw  Error ("no such property/method" )         }     } }, pobj=new  Proxy (obj,handlers) pobj.a=3 ; pobj.foo(); pobj.b=4 ; 
 
代理在后: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 var  handlers={    get ( ) {         throw  "no such property/method"      },     set ( ) {         throw  "no such property/method"      } } var  pobj=new  Proxy ({},handlers);var  obj={    a :1 ,     foo ( ) {         console .log("a:" ,this .a)     } } Object .setPrototypeOf(obj,pobj);obj.a=3 ; obj.foo(); obj.b=4 ; 
 
代理实现多个[[Prototype]]链接 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 var  obj1={    name :"obj-1" ,     foo ( ) {         console .log("obj1.foo" ,this .name)     } },obj2={     name :"obj-2" ,     foo ( ) {         console .log("obj2.foo" ,this .name)     },     bar ( ) {         console .log("obj2.bar" ,this .name)     }    }, handlers={     get (target,key,context ) {         if (Reflect .has(target,key)){             return  Reflect .get(target,key,context)         }else {                          for (let  p of  target[Symbol .for("[[Prototype]]" )]){                 if (Reflect .has(p,key)){                     return  Reflect .get(p,key,context)                 }             }         }     } }, obj3=new  Proxy ({     name :"obj3" ,     baz ( ) {         this .foo();         this .bar()     } },handlers) obj3[Symbol .for("[[Prototype]]" )]=[obj1,obj2]; obj3.baz();