Vue3.0中双向绑定的实现从Object.defineProperty变为ES6中的Proxy。其实Proxy和definePrototype在平常业务场景中还是挺少用到,但是作为前端工程师这一块是一个主流框架的底层原理。所以还是必须要了解这两种方法。
一、Proxy
1. 什么是 Proxy
- MDN 中定义:Proxy 对象用于创建一个对象的代理,从而实现基本的拦截和自定义(如属性查找、赋值、枚举、函数调用等)
 - ES6 入门中定义:Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”,即对编程语言进行编程。
 
Proxy可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问都必须通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy原意就是代理,可以理解为代理某些操作。
2. 语法
1  | const p = new Proxy(target, handler);  | 
target:就是你要是用Proxy包装的目标对象(可以是任何类型的对象,包括函数、数组,甚至是另一个代理)。handler:是一个对象,里面有各种你代理的方法。
3. 一个例子
1  | let obj = {  | 
- 输出结果

 - target:就是你代理的对象。
 - propKey:要读取的属性名。
 - value:要设置的新 value。
 - receiver:Proxy 或者继承 Proxy 的对象。
 
4. Proxy 的支持的拦截操作
get(target, propKey, receiver):拦截对象属性的读取,如proxy.fooproxy['foo']
set(target, propKey, value, receiver):返回一个布尔值。拦截对象属性的设置,如proxu.foo = vproxy['foo'] = v,
has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。deleteProperty(target, propKey):返回一个布尔值,拦截以下方法delete proxy[propKey]
ownKeys(target):返回一个数组,该方法返回目标对象所有的属性的属性名,而Obejct.keys()的返回结果仅包括目标对象自身的可属性。拦截以下方法Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy)for...in循环
getOwnPropertyDescriptor(target, propKey):返回属性的描述对象,拦截以下方法Object.getOwnPropertyDescriptor(proxy, propKey)
defineProperty(target, propKey, propDesc):返回一个布尔值,拦截以下方法Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs)
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实例作为函数调用的操作,比如proxy(...args)proxy,call(object, ...args)proxy.apply(...)。
construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。
注:描述对象指value、writable、enumerable、configurable、get、set这六个描述对象。
二、Reflect
说来惭愧,笔者在很长一段时间里,都没有意识到前端还有Reflect这一模块,用模块来形容Reflect是因为,它长得是一个对象的样子,也很像一个函数对象,但很抱歉的是,他没有new方法,他就是一个普普通通的对象。
1. 什么是 Reflect
- MDN 中的定义
:Reflect是一个内置的对象,它提供拦截JavaScript操作的方法。这些方法与proxy handlers的方法相同。Reflect不是一个函数对象,因此它是不可构造的。 - ES6 中的定义
:Reflect对象与Proxy很相似,都是ES6为了操作对象而提供的新的 API。 
2. 如何使用
首先,我们要了解,为什么要创造出Reflect。ES6 中列出了Reflect对象设计的意义。
- 将
Object中明显属于语言内部的方法如Object.defineProperty放到Reflect对象中来。现阶段,某些方法同时在Object和Reflect对象上部署,未来新方法指将部署在Reflect对象上,可以说Reflect上可以找到语言内部的方法。 - 修改某些
Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回 false。 - 让
Object操作都变成函数行为。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为。 
1  | "assign" in Object; // true;  | 
Reflect对象的方法Proxy对象的方法,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。也就是说,不管Proxy怎么修改默认行为,你总可以在Reflect上获取默认行为。
1  | var loggedObj = new Proxy(obj, {  | 
3. Reflect 的支持的拦截操作
与Proxy支持的操作完全相同
三、Object.defineProperty
1. 什么是 Object.defineProperty
- MDN 中的定义
:Object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。 
1  | const object1 = {};  | 
2. 使用
1  | Object.defineProperty(obj, prop, descriptor);  | 
- obj:要定义属性的对象
 - prop:要定义或修改的属性的名称或
Symbol - descriptor:要定义或修改的属性描述符。