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') // trueReflect.construct(target, args)
Reflect.construct方法对应new target(...args),用来新建一个实例对象。接受两个参数,target对象,args数组。
function F(){
this.foo = 'bar'
}
// 旧写法
let instance1 = new F()
// 新写法
let instance2 = 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__ // trueReflect.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: 2Reflect.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) // false二者的区别在于,如果参数不是对象,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对象,当对象属性被设置时,会触发所有注册的观察者函数执行。