Reflect
Reflect
对象不仅提供了一种更加清晰和一致的方式来操作对象属性,还与 Proxy
对象紧密协作,为开发者提供了强大的元编程能力。本文阮一峰老师将深入探讨 Reflect
的静态方法、其与 Proxy
的关系以及实际应用,揭示如何在 JavaScript 中更有效地使用这些强大的工具。
概述
Reflect
对象与Proxy
对象一样,也是 ES6 为了操作对象而提供的新 API。Reflect
对象的设计目的有以下几个。
- 将
Object
对象的一些明显属于语言内部的方法(比如Object.defineProperty
),放到Reflect
对象上。现阶段,某些方法同时在Object
和Reflect
对象上部署,未来的新方法将只部署在Reflect
对象上,即语言内部的方法最好从Reflect
对象上拿。 - 修改某些方法返回的结果。如
Object.defineProperty(obj,name,desc)
在无法定义属性时,会抛出一个错误,为了解决错误不得不用try...catch
捕获;而Reflect.defineProperty(obj,name,desc)
则会返回false
,只需要if...else
判断。 - 让
Object
操作变成函数行为。某些Object
操作是命令式,比如name in obj
和delete obj[name]
,而Reflect.has(obj,name)
和Reflect.deleteProperty(obj,name)
让它们变成了函数行为。 Reflect
对象的方法与Proxy
对象的方法一一对应,只要是Proxy
对象的方法,就能在Reflect
对象上找到对应的方法。这就让Proxy
对象可以方便地调用对应的Reflect
方法,完成默认行为,作为修改行为的基础。也就是说,不管Proxy
怎么修改默认行为,你总可以在Reflect
上获取默认行为。
静态方法
Reflect
对象一共有13个静态方法。
Reflect.get(target,name,receiver)
Reflect.get
方法查找并返回target
对象的name
属性键值,如果没有该属性,则返回undefined
。接受三个参数,target
对象,name
属性名,receiver
可选参数。
let obj1 = {
foo: 1,
bar: 2,
get baz() {
return this.foo + this.bar;
}
}
Reflect.get(obj1,'foo') // 1
Reflect.get(obj1,'bar') // 2
Reflect.get(obj1,'baz') // 3
如果target
对象中指定了getter
,Reflect.get
会以receiver
为上下文调用getter
函数。
let obj1 = {
foo: 1,
bar: 2,
get baz() {
return this.foo + this.bar;
}
}
let obj2 = {
foo: 4,
bar: 5
}
Reflect.get(obj1,'baz', obj2) // 9
如果 target
参数不是一个对象,Reflect.get
会抛出一个 TypeError
异常错误。
Reflect.set(target,name,value,receiver)
Reflect.set
方法给 target
对象的 name
属性赋值 value
,如果成功则返回true
,否则返回false
。接受四个参数,target
对象,name
属性名,value
属性值,receiver
可选参数。
let obj = {
foo: 1
}
Reflect.set(obj,'foo',2)
obj.foo // 2
如果 target
对象中指定了 setter
,Reflect.set
会以 receiver
为上下文调用 setter
函数。
let obj = {
foo: 1,
set foo(val) {
this.foo = val + 1;
}
}
let data = {
foo: 4
}
Reflect.set(obj,'foo',2,data)
data.foo // 3
obj.foo // 1
如果 target
参数不是一个对象,Reflect.set
会抛出一个 TypeError
异常错误。
⚠ 注意
Reflect.set
会触发 Proxy.defineProperty
的拦截。
Reflect.has(target,name)
Reflect.has
方法对应 name in obj
,用来判断对象是否具有某个属性,如果具有则返回 true
,否则返回 false
。接受两个参数,target
对象,name
属性名。
let obj = {
foo: 1
}
Reflect.has(obj,'foo') // true
Reflect.has(obj,'bar') // false
如果 target
参数不是一个对象,Reflect.has
会抛出一个 TypeError
异常错误。
Reflect.deleteProperty(target,name)
Reflect.deleteProperty
方法用于删除对象的属性,返回一个布尔值。如果成功删除或者要删除的属性不存在,则返回 true
,如果删除失败或者删除的属性依旧存在,则返回 false
。接受两个参数,target
对象,name
属性名。
let obj = {
foo: 1
}
// 旧写法
delete obj.foo // true
// 新写法
Reflect.deleteProperty(obj,'foo') // true
Reflect.deleteProperty(obj,'bar') // true
Reflect.construct(target, args)
Reflect.construct
方法对应new target(...args)
,用来新建一个实例对象。接受两个参数,target
对象,args
数组。
function F(){
this.foo = 'bar'
}
// 旧写法
let instance = new F()
// 新写法
let instance = Reflect.construct(F,[])
Reflect.getPrototypeOf(target)
Reflect.getPrototypeOf
方法用于读取对象的 __proto__
属性,对应 Object.getPrototypeOf(obj)
。接受一个参数,target
对象。
let obj = {}
Reflect.getPrototypeOf(obj) === Object.prototype // true
Reflect.getPrototypeOf(obj) === obj.__proto__ // true
Reflect.getPrototypeOf
与 Object.getPrototypeOf
二者的区别是,如果参数不是对象,Object.getPrototypeOf
会将参数转为对象,然后再运行,而 Reflect.getPrototypeOf
会报错。
Reflect.setPrototypeOf(target,prototype)
Reflect.setPrototypeOf
方法用于设置对象的 __proto__
属性,对应 Object.setPrototypeOf(obj, newProto)
。接受两个参数,target
对象,prototype
对象。
let obj = {}
Object.setPrototypeOf(obj,Object.prototype) // 旧写法
Reflect.setPrototypeOf(obj,Object.prototype) // 新写法
如果 target
参数不是对象,Object.setPrototypeOf
会返回第一个参数本身,Reflect.setPrototypeOf
会报错。如果 target
参数是 undefined
或者 null
,两者都会抛出报错。
Reflect.apply(target,thisArg,args)
Reflect.apply
方法等同于 Function.prototype.apply.call(func,thisArg,args)
,用于绑定 this
对象后执行函数。接受三个参数,target
对象,thisArg
对象,args
数组。
let doSomething = function (foo,bar){
console.log('foo:',foo)
console.log('bar:',bar)
}
Reflect.apply(doSomething,null,[1,2])
// foo: 1
// bar: 2
Reflect.defineProperty(target,name,desc)
Reflect.defineProperty
方法基本等同于 Object.defineProperty
,用来为对象定义属性。接受三个参数,target
对象,name
属性名,desc
属性描述符。
function myReflactDefineProperty(target,name,desc){
// 某些操作
return Reflect.defineProperty(target,name,desc)
}
如果 target
参数不是对象,方法会抛出一个 TypeError
异常错误。
Reflect.getOwnPropertyDescriptor(target,name)
Reflect.getOwnPropertyDescriptor
方法基本等同于 Object.getOwnPropertyDescriptor
,用于得到指定属性的描述对象,将来会替代掉 Object.getOwnPropertyDescriptor
方法。接受两个参数,target
对象,name
属性名。
const obj = {
foo: 1
}
Reflect.getOwnPropertyDescriptor(obj,'foo')
// {value: 1, writable: true, enumerable: true, configurable: true}
二者的区别在于,如果 target
参数不是对象,Object.getOwnPropertyDescriptor
会返回 undefined
,而 Reflect.getOwnPropertyDescriptor
会报错,表示参数非法。
Reflect.isExtensible(target)
Reflect.isExtensible
方法对应 Object.isExtensible
,返回一个布尔值,表示当前对象是否可扩展。接受一个参数,target
对象。
const obj = {}
Object.isExtensible(obj) // true
Reflect.isExtensible(obj) // true
二者的区别在于,如果参数不是对象,Object.isExtensible
会返回 false
,而 Reflect.isExtensible
会报错,表示参数非法。
Reflect.preventExtensions(target)
Reflect.preventExtensions
方法对应 Object.preventExtensions
,用于让一个对象变为不可扩展。接受一个参数,target
对象。
const obj = {}
Object.preventExtensions(obj) // object {}
Reflect.isExtensible(obj) // true
二者的区别在于,如果参数不是对象,ES5环境下的 Object.preventExtensions
会报错,ES6环境下会返回原对象;而 Reflect.preventExtensions
会报错。
Reflect.ownKeys(target)
Reflect.ownKeys
方法返回对象的所有属性,基本等同于 Object.getOwnPropertyNames
与 Object.getOwnPropertySymbols
之和。
const obj = {
foo: 'bar',
[Symbol.for('baz')]: 42
}
Reflect.ownKeys(obj)
// ["foo", Symbol(baz)]
实例运用
下面是 Reflect
对象与 Proxy
对象结合使用的一个例子。实现观察者模式。
const queuedObservers = new Set()
const observe = fn => queuedObservers.add(fn)
const observable = obj => new Proxy(obj,{
set(target,key,value,receiver){
const result = Reflect.set(target,key,value,receiver)
queuedObservers.forEach(observer => observer())
return result
}
})
const person = observable({
name: '张三',
age: 20
})
function print(){
console.log(`${person.name}, ${person.age}岁`)
}
observe(print)
person.name = '李四'
// 李四, 20岁
person.age = 30
// 李四, 30岁
总结
Reflect
对象是 ES6 引入的新 API,用于操作对象。Reflect
对象有以下几个设计目的:
- 将
Object
对象的一些内部方法转移到Reflect
对象上 - 修改某些方法的返回结果,使其更加友好
- 将
Object
操作转化为函数行为 - 与
Proxy
对象的方法一一对应,方便Proxy
调用对应的Reflect
方法。
Reflect
对象提供了13个静态方法,包括Reflect.get
、Reflect.set
、Reflect.has
、Reflect.deleteProperty
等,这些方法分别对应于对象属性的读取、设置、存在性检查和删除。此外,还有Reflect.construct
用于构造函数的调用,Reflect.getPrototypeOf
和Reflect.setPrototypeOf
用于读取和设置对象的原型,Reflect.apply
用于函数的apply
操作,Reflect.defineProperty
和Reflect.getOwnPropertyDescriptor
用于定义和获取对象属性的描述符。Reflect.isExtensible
和Reflect.preventExtensions
用于检查和阻止对象的扩展,而Reflect.ownKeys
返回对象的所有属性。
最后,通过一个实例展示了如何将Reflect
与Proxy
结合使用,实现观察者模式,即当对象属性发生变化时,自动通知所有观察者。这个例子中,observable
函数返回一个Proxy
对象,当对象属性被设置时,会触发所有注册的观察者函数执行。