是什么
在 ES5 出现之前,JavaScript 环境中的对象包含许多不可枚举和不可写的属性,但开发者不能定义自己的不可枚举或不可写属性,于是 ES5 引入了 Object.defineProperty() 方法(此方法也是 Vue 2 实现响应式的核心 API)来支持开发者去做 JavaScript 引擎早就可以实现的事情。
ES6 添加了一些内建对象,赋予开发者更多访问 JavaScript 引擎的能力。Proxy 是一种可以拦截并改变底层 JavaScript 引擎操作的包装器,在新语言中通过它暴露内部运作的对象,从而让开发者可以创建内建的对象。
Proxy 要解决的问题
在 ES6 出现以前,开发者不能通过自己定义的对象模仿 JavaScript 数组对象的行为方式:当给数组的特定元素赋值时,影响到该数组的 length 属性,也可以通过 length 属性修改数组元素。
const animals = ["cat", "dog", "pig"];
console.log(animals.length); // 3
animals[3] = "rabbit";
console.log(animals.length); // 4
animals.length = 2;
console.log(animals.length); // 2
console.log(animals[3]); // undefined
console.log(animals[2]); // undefined
console.log(animals[1]); // "dog"
在 ES5 之前开发者无法自己实现这些行为,但现在通过代理就可以了。
数值属性和 length 属性具有这种非标准行为,因而在 ES6 中数组被认为是奇异对象(exotic object,与普通对象相对)。
Proxy and Reflect
调用 new Proxy() 可创建代替其他目标(target)对象的代理,它虚拟化了目标,所以二者看起来功能一致。
代理可以拦截 JavaScript 引擎内部目标的底层对象操作,这些底层操作被拦截后会触发响应特定操作的陷阱函数。
反射 API 以 Reflect 对象的形式出现,对象中方法的默认特性与相同的底层操作一致,而代理可以覆写这些操作,每个代理陷阱对应一个命名和参数都相同的 Reflect 方法。
代理陷阱 | 覆写的特性 | 默认特性 |
---|---|---|
get | 读取一个属性值 | Reflect.get() |
set | 写入一个属性 | Reflect.set() |
has | in 操作符 | Reflect.has() |
deleteProperty | delete 操作符 | Reflect.deleteProperty() |
getPrototypeOf | Object.getPrototypeOf() | Reflect.getPrototypeOf() |
setPrototypeOf | Object.setPrototypeOf() | Reflect.setPrototypeOf() |
isExtensible | Object.isExtensible() | Reflect.isExtensible() |
preventExtensions | Object.preventExtensions() | Reflect.preventExtensions() |
getOwnPropertyDescriptor | Object.getOwnPropertyDescriptor() | Reflect.getOwnPropertyDescriptor() |
defineProperty | Object.defineProperty() | Reflect.defineProperty() |
ownKeys | Object.keys()、Object.getOwnPropertyNames() 和 Object.getOwnPropertySymbols() | Reflect.ownKeys() |
apply | 调用一个函数 | Reflect.apply() |
construct | 用 new 调用一个函数 | Reflect.construct() |
创建一个简单的代理
Proxy 构造函数创建代理需要传入两个参数:target、handler(s) object。
let target = {
};
let proxy = new Proxy(target, {
});
proxy.name = "Cherie";
console.log(proxy.