Reflect
Reflect 对象与 Proxy 对象一样是 ES6 提出的新 API,它可以简化 Proxy 的创建。
对象上有一些内部方法:[[Get]] 和 [[Set]],我们不能直接调用。Reflect 对象则使调用这些内部方法成为了可能。
let user = {}
Reflect.set(user, 'name', 'John')
alert(user.name) // John以下是执行相同操作和 Reflect 调用的示例:
| 操作 | Reflect 调用 | 内部方法 |
|---|---|---|
| obj[prop] | Reflect.get(obj, prop) | [[Get]] |
| obj[prop] = value | Reflect.set(obj, prop, value) | [[Set]] |
| delete obj[prop] | Reflect.deleteProperty(obj, prop) | [[Delete]] |
| new F(value) | Reflect.construct(F, value) | [[Construct]] |
| … | … | … |
Reflect 的设计目的
将
Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到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)让它们变成了函数行为。Reflect对象的方法与Proxy的方法一一对应。对于每个可被
Proxy捕获的内部方法,在Reflect中都有一个对应的方法,其名称和参数与Proxy捕捉器相同。我们可以很方便地使用Reflect来将操作转发给原始对象。
代理一个 getter
我们先看一个原型链的例子:
const person = {
_name: 'person',
get name() {
return this._name
},
}
const keqing = {
_name: 'keqing',
__proto__: person,
}
keqing.name // keqing在上面的代码中,keqing 对象上并没有 name 属性,但是我们可以通过原型链访问到 person 对象上的 name getter。这时候,这个 getter 中的 this 指向的是 keqing 对象!
来看使用 Proxy 代理后的例子:
const person = {
_name: 'person',
get name() {
return this._name
},
}
const proxy = new Proxy(person, {
get(target, key, receiver) {
console.log(receiver === keqing) // true
return target[key]
},
})
const keqing = {
_name: 'keqing',
__proto__: proxy,
}
keqing.name // person我们发现,此时 this 指向了 person!这其实是理所当然的,虽然 keqing 对象通过原型链访问其原型对象的 name 属性,但是被 Proxy 代理之后,直接返回 target[key],那么 this 当然就是 target 代表的对象 person 了。
怎么解决这个问题呢?关键在于如何获取正确的 this。幸运地是,get 捕捉器的第三个参数 receiver 就是正确的 this。
我们可以使用 Reflect 对象的 get 方法将正确的 this 传递给 getter:
const person = {
_name: 'person',
get name() {
return this._name
},
}
const proxy = new Proxy(person, {
get(target, key, receiver) {
return Reflect.get(target, key, receiver)
},
})
const keqing = {
_name: 'keqing',
__proto__: proxy,
}
keqing.name // keqing我们可以写得更短:
get(target, prop, receiver) {
return Reflect.get(...arguments);
}总结
Reflect 调用的命名与捕捉器的命名完全相同,并且接受相同的参数。它提供了一个安全的方式,可以轻松地转发操作,并确保我们不会忘记与此相关的任何内容。