Proxy 与 Reflect:最硬核、最实用的解释
基本概念
Proxy
用来拦截对象的各种操作,包括读、写、删、函数调用等。类似于代理人,任何对对象的操作都要经过它。
js
const proxy = new Proxy(target, handler)
target:要代理的目标对象handler:一个对象,包含了拦截目标对象的函数。
常用的代理操作有读取属性(get)、属性设置(set)、存在属性(has)、属性删除(deleteProperty)、函数调用(apply)
js
const handler = {
get: function(target,property, receiver) {
return target[property];
},
set:function(target,property,value,receiver) {
target[property] = value;
return true;
},
has: function(target,property) {
return property === 'name' ? true : false
},
deleteProperty: function(target, property) {
delete target[property]
return true
},
apply:function(target, thisArg,argumentsList) {
return target(argumentsList[0],argumentsList[1]) * 10
}
}
const obj = {
b: 10,
get a() {
return this.b;
}
}
const proxy = new Proxy(obj, handler);
console.log(proxy.a)
Reflect
Reflect 是一个"工具类",是 ES6 新增的一个"内置操作 API 集 ",它把 JavaScript 引擎内部对对象执行的各种默认操作统一成了一套方法。提供与 Proxy 拦截方法 一一对应 的默认操作。
js
Reflect.get(target, key)
Reflect.set(target, key, value)
Reflect.deleteProperty(target, key)
为什么使用Proxy?
相比于Object.defineProperty(),Proxy 能够拦截对象的所有行为。
为什么使用Reflect?
在 ES6 之前:
- JS 引擎内部执行 getter、setter、delete、in 等操作都有 一套规范行为
- 但 JS 开发者没有 API 可以直接调用这些规范操作
Proxy 出现后有个问题:
Proxy 的 get/set/delete 等拦截器,必须决定是:
- 继续执行默认行为
- 还是替换行为
但以前开发者无法"执行默认行为",只能写:
js
target[key]
target[key] = value
delete target[key]
这些都不是 ECMA 规范层面的真正操作,会破坏:
- 原型链查找规则
- getter/setter 的 this 绑定
- receiver 的传递
- 返回值语义
- 抛错语义
于是 Reflect 出现,用于:
提供所有内部操作的"规范等价方法"
为什么Proxy和Reflect经常搭配使用?
- 因为 Proxy 拦截后,需要用 Reflect 执行"默认行为"
- 因为 Reflect 确保 this、原型链、返回值都符合语言规范
- 因为 Reflect 参数与 Proxy handler 完全一致,天生配套
- 因为直接操作 target 会导致 getter/setter/原型链错误
js
const p = new Proxy(obj, {
get(target, key, receiver) {
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
console.log('setting', key);
return Reflect.set(target, key, value, receiver);
}
});