ES6新增的代理与反射为开发者提供了拦截并向基本操作中嵌入额外行为的能力。在对目标对象的各种操作影响目标之前,可以在代理对象中对这些操作加以控制。

一、代理模式
代理是目标对象的抽象,即时目标对象的替身,有完全独立于目标对象,目标对象既可以直接被操作,又可以通过代理来操作
1.创建空代理
使用Proxy构造函数创建,第一个参数为目标对象,第二个参数为处理程序对象,缺少任何一个参数都会报TypeError
Proxy.prototype为undefined;严格相等区分代理和目标

2.定义捕获器
捕获器:处理程序对象中定义的“基本操作的拦截器”,每个处理程序对象中可以定义任意数量捕获器,每一个捕获器对应一个基本操作,可以直接或简接在代理对象上调用。代理在将操作传给目标对象之前会调用捕获器函数
3.捕获器参数和反射api
get()捕获器接受目标对象、要查询的属性、代理对象三个参数

所有捕获器都可以基于自己的参数重建原始操作,但开发者手写太过复杂,可以通过调用全局Reflect对象同名方法重建

4.捕获器不变式
每个捕获器都知道目标对象上下文、捕获函数签名,而捕获处理程序的行为必须遵守“捕获器不变式”,通常因方法而异,但防止了捕获器定义出现反常行为
5.可撤销代理
通过new Proxy()创建的代理关系在代理对象生命周期内会一直持续存在。
revocable()方法支持撤销代理对象和目标对象之间的关联,且撤销操作不可逆,这个函数也是幂等的
撤销函数和代理对象是在实例化时同时生成的

6.实用反射api
1)反射api和对象api
反射api并不局限于捕获处理程序
大多数反射api方法在Object类型上有对应方法。反射api适用于细粒度的对象控制与操作,Object上api适用于通用程序
2)很多反射api返回操作的“状态标记”布尔值,表示意图的操作是否成功
提供状态标记的方法
Reflect.defineProperty()
Reflect.preventExtensions()
Reflect.setPrototypeOf()
Reflect.set()
Reflect.deleteProperty()
3)用一等函数代替操作符
Reflect.get()
Reflect.set() =
Reflect.has() in with
Reflect.deleteProperty() delete
Reflect.construct() new
4)安全地运用函数
通过apply调用函数时,被调用的函数可能本身也定义了自身的apply方法
为绕过这个问题 Function.prototype.apply.call(myFunc, thisVal, arguments)
或者 Reflect.apply(myFunc, thisVal, arguments)
7.代理另一个代理

8.代理的问题与不足
1)代理中的this
如使用weakset实现私有变量时,因为实例是将目标对象本身作为WeakMap()的键,代理对象则无法从本身取出这个值。
为解决,只能代理类,而不能代理实例


2)代理与内部槽位
一些内置对象可能会依赖代理无法控制的机制,导致在代理上调用某些方法出错
Date类型方法执行依赖于this值上的[ [ NumberDate] ] 内部槽位,而代理对象上不存在这个槽位,这个内部槽位的值无法通过set() 和get()访问,因此代理拦截后转发给目标对象会报错
二、代理捕获器和反射方法
代理可以捕获13种不同的基本操作。
1)get() Reflect.get()
返回值:无限制;
拦截规则:proxy.property proxy[property] Object.create(proxy)[property] Reflect.get(proxy, property, receiver)
捕获器处理程序参数: target, property, proxy
捕获不变式:
如果target.property不可写且不可配置,则返回值必须与target.property匹配
如果target.property不可配置,且[ [ get ] ] 特性为undefined, 则处理程序返回值也必须是undefined
2) set() Reflect.set()
返回值:标志状态
拦截操作: proxy.property = val ; proxy[property] = val ; Object.create(proxy)[property] = val; Reflect.set(proxy, property, value, receiver)
捕获器参数: target, property, value, receiver(接受最初赋值的对象)
捕获器不变式:
如果不可写且不可配置,则不能修改目标属性的值
如果不可写,且[ [set ] ] 为undefined, 则不能修改目标值
3)has() Reflect.has()
返回值:布尔值,表示属性是否存在
拦截操作:property in proxy; property in Object.create(proxy); with(proxy); Reflect.has(proxy, property)
捕获器参数: target, property
捕获器不变式:
如果存在且不可配置,必须返回true
如果存在且不可扩展,必须返回true
4) defineProperty() Reflect.defineProperty()
会在Object.defineProperty()中被调用
返回值:标识状态
拦截的操作:Object.defineProperty(proxy, property, descriptor) ; Reflect.defineProperty(proxy, property, descriptor)
参数:target, property, descriptor
不变式:
如果不可扩展,则无法定义属性
如果对象有一个可配置的属性,则不同添加同名的不可配置属性
如果对象有一个不可配置的属性,则不同添加同名的可配置属性
5)getOwnPropertyDescriptor() Reflect.getOwnPropertyDescriptor()
会在Object.getOwnPropertyDescriptor()中调用
返回值:返回一个对象,或者属性不存在时返回undefined
拦截操作:Object.getOwnPropertyDescriptor(proxy, property);Reflect.getOwnPropertyDescriptor(proxy,property)
参数:target,property
6)deleteProperty() Reflect.deleteProperty()
会在delete操作符中被调用
返回值:标识状态
拦截:delete proxy.property; delete proxy[property] ; Reflect.deleteProperty(proxy, property)
参数:target, property
7) ownKeys() Reflect.ownKeys()
会在Object.keys()中调用
返回值:必须返回包含字符串或者符号的可枚举对象
拦截:Object.getOwnPropertyNames(proxy); Object.getOwnPropertySymbols(proxy); Object.keys(proxy);Reflect.ownKeys(proxy)
参数:target
8)getPrototypeOf() Reflect.getPrototypeOf()
返回值:对象或者null
拦截:Object.getPrototypeOf(proxy);Reflect.getPrototypeOf(proxy);proxy.__proto__;Object.prototype.isPrototypeOf(proxy);proxy instanceof Object
参数:target
9)setPrototypeOf()
返回值:标识状态
拦截:Object.setPrototypeOf(); Reflect.setPrototypeOf()
参数:target, prototype
10)isExtensible()
返回值:布尔值,表示是否可扩展
拦截:Object./Reflect.isExtensible(proxy)
参数:target
11)preventExtensions()
返回值:布尔值,表示是否已经不可扩展
拦截:Object./Reflect.preventExtensions(proxy)
参数:target
12)apply()
会在调用函数时被调用 Reflect.apply()
返回值:无限制
拦截: proxy(…argumentsList)
Function.prototype.apply(thisArg, argumentsList)
Function.prototype.call(thisArg, …argumentsList)
Reflect.apply(target, thisArgument, argumentsList)
参数:target, thisArg, argumentsList
不变式:target必须是函数对象
13)construct()
会在new操作符中被调用
返回:一个对象
拦截: new Proxy(…argumentsList); Reflect.construct(target, arguments, newTarget)
参数:target(目标构造函数);arguments(传给构造函数的参数列表);newTarget:最初被调用的构造函数
不变式:target必须是可以用做构造函数的函数
三、代理模式
1.跟踪属性访问:
通过捕获set get has等操作,可以知道对象什么时候被访问、被查询
2.隐藏属性
代理的内部实现是对外部代码不可见的,因此可以隐藏目标对象上的属性

3.属性验证
4.函数与构造函数参数验证
5.数据绑定和可观察对象
1)如将被代理的类绑定到应给全局实例集合,让创建的所有实例都被添加到这个集合中
2)如,把集合绑定到一个事件分派程序中,每次插入新实例都会发送消息
