Reflect 是 ES6 新增的内置对象,提供了拦截 JavaScript 操作的方法,与 Proxy handler 方法一一对应。
为什么需要 Reflect#
在 Reflect 之前,对象的元操作分散在 Object、Function、操作符等多处:
// 检查属性'name' in objObject.prototype.hasOwnProperty.call(obj, 'name')
// 删除属性delete obj.name
// 获取属性obj.nameobj['name']
// 调用函数fn.apply(thisArg, args)fn.call(thisArg, ...args)Reflect 将这些操作统一为函数形式:
Reflect.has(obj, 'name')Reflect.deleteProperty(obj, 'name')Reflect.get(obj, 'name')Reflect.apply(fn, thisArg, args)Reflect 的优势#
1. 返回值更合理#
// Object.defineProperty 失败时抛异常try { Object.defineProperty(Object.freeze({}), 'name', { value: 1 })} catch (e) { console.log('失败')}
// Reflect.defineProperty 返回 falseif (!Reflect.defineProperty(Object.freeze({}), 'name', { value: 1 })) { console.log('失败')}2. 函数式操作#
// 操作符形式'name' in objdelete obj.name
// 函数形式(更适合函数式编程)Reflect.has(obj, 'name')Reflect.deleteProperty(obj, 'name')3. 与 Proxy 配合#
const proxy = new Proxy(target, { get(target, property, receiver) { console.log(`读取 ${property}`) // Reflect 确保正确的 this 绑定 return Reflect.get(target, property, receiver) },})13 个静态方法#
Reflect.get()#
const obj = { name: '张三', get fullName() { return this.name + '先生' },}
Reflect.get(obj, 'name') // '张三'
// 第三个参数 receiver 会作为 getter 的 thisconst receiver = { name: '李四' }Reflect.get(obj, 'fullName', receiver) // '李四先生'Reflect.set()#
const obj = { _name: '张三', set name(value) { this._name = value },}
Reflect.set(obj, 'name', '李四') // trueconsole.log(obj._name) // '李四'
// 使用 receiverconst receiver = { _name: '' }Reflect.set(obj, 'name', '王五', receiver)console.log(receiver._name) // '王五'console.log(obj._name) // '李四'(未改变)Reflect.has()#
const obj = { name: '张三' }
Reflect.has(obj, 'name') // trueReflect.has(obj, 'age') // false
// 等价于 in 操作符'name' in obj // trueReflect.deleteProperty()#
const obj = { name: '张三', age: 25 }
Reflect.deleteProperty(obj, 'age') // trueconsole.log(obj) // { name: '张三' }
// 删除不存在的属性也返回 trueReflect.deleteProperty(obj, 'notExist') // true
// 删除不可配置的属性返回 falseObject.defineProperty(obj, 'id', { value: 1, configurable: false })Reflect.deleteProperty(obj, 'id') // falseReflect.construct()#
function Person(name) { this.name = name}
// 等价于 new Person('张三')const person = Reflect.construct(Person, ['张三'])console.log(person.name) // '张三'
// 可以指定 new.targetclass Animal {}class Dog extends Animal {}
const instance = Reflect.construct(Animal, [], Dog)console.log(instance instanceof Dog) // trueReflect.getPrototypeOf()#
const obj = {}Reflect.getPrototypeOf(obj) === Object.prototype // true
// 与 Object.getPrototypeOf 的区别// Object.getPrototypeOf('string'); // 强制转换为对象// Reflect.getPrototypeOf('string'); // TypeErrorReflect.setPrototypeOf()#
const obj = {}const proto = { greet() { return 'Hello' },}
Reflect.setPrototypeOf(obj, proto) // trueobj.greet() // 'Hello'
// 设置 nullReflect.setPrototypeOf(obj, null) // true
// 对冻结对象返回 falseconst frozen = Object.freeze({})Reflect.setPrototypeOf(frozen, {}) // falseReflect.apply()#
function greet(greeting, punctuation) { return `${greeting}, ${this.name}${punctuation}`}
const context = { name: '张三' }
Reflect.apply(greet, context, ['你好', '!'])// '你好, 张三!'
// 等价于greet.apply(context, ['你好', '!'])greet.call(context, '你好', '!')Reflect.defineProperty()#
const obj = {}
const result = Reflect.defineProperty(obj, 'name', { value: '张三', writable: true, configurable: true, enumerable: true,})
console.log(result) // trueconsole.log(obj.name) // '张三'
// 失败时返回 falseconst frozen = Object.freeze({})Reflect.defineProperty(frozen, 'x', { value: 1 }) // falseReflect.getOwnPropertyDescriptor()#
const obj = { name: '张三' }
Reflect.getOwnPropertyDescriptor(obj, 'name')// {// value: '张三',// writable: true,// enumerable: true,// configurable: true// }
// 不存在的属性返回 undefinedReflect.getOwnPropertyDescriptor(obj, 'age') // undefinedReflect.isExtensible()#
const obj = {}Reflect.isExtensible(obj) // true
Object.preventExtensions(obj)Reflect.isExtensible(obj) // falseReflect.preventExtensions()#
const obj = {}Reflect.preventExtensions(obj) // trueReflect.isExtensible(obj) // false
// 已经不可扩展的对象Reflect.preventExtensions(obj) // true(仍返回 true)Reflect.ownKeys()#
const sym = Symbol('id')const obj = { name: '张三', [sym]: 123,}
Reflect.ownKeys(obj) // ['name', Symbol(id)]
// 包含不可枚举属性Object.defineProperty(obj, 'hidden', { value: 'secret', enumerable: false,})
Reflect.ownKeys(obj) // ['name', 'hidden', Symbol(id)]与 Proxy 配合#
Reflect 方法与 Proxy handler 一一对应,配合使用可以确保正确的行为:
const target = { _name: '张三', get name() { return this._name }, set name(value) { this._name = value },}
const proxy = new Proxy(target, { get(target, property, receiver) { console.log(`GET: ${String(property)}`) // 使用 receiver 确保 getter 中 this 正确指向 proxy return Reflect.get(target, property, receiver) },
set(target, property, value, receiver) { console.log(`SET: ${String(property)} = ${value}`) return Reflect.set(target, property, value, receiver) },})
proxy.name // GET: name, 返回 '张三'proxy.name = '李四' // SET: name = 李四为什么需要 receiver#
const parent = { _value: 'parent', get value() { return this._value },}
const child = { _value: 'child',}
Object.setPrototypeOf(child, parent)
// 不使用 receiverconst proxy1 = new Proxy(parent, { get(target, property) { return Reflect.get(target, property) },})
Object.setPrototypeOf(child, proxy1)child.value // 'parent'(错误!应该是 'child')
// 使用 receiverconst proxy2 = new Proxy(parent, { get(target, property, receiver) { return Reflect.get(target, property, receiver) },})
Object.setPrototypeOf(child, proxy2)child.value // 'child'(正确!)实战应用#
通用代理#
function createObservable(target, handlers = {}) { return new Proxy(target, { get(target, property, receiver) { handlers.onGet?.(target, property) return Reflect.get(target, property, receiver) },
set(target, property, value, receiver) { const oldValue = Reflect.get(target, property, receiver) const result = Reflect.set(target, property, value, receiver) if (result && oldValue !== value) { handlers.onChange?.(target, property, value, oldValue) } return result },
deleteProperty(target, property) { const result = Reflect.deleteProperty(target, property) if (result) { handlers.onDelete?.(target, property) } return result }, })}
const data = createObservable( { count: 0 }, { onGet(target, prop) { console.log(`读取: ${prop}`) }, onChange(target, prop, newVal, oldVal) { console.log(`变化: ${prop} ${oldVal} -> ${newVal}`) }, })
data.count // 读取: countdata.count = 1 // 变化: count 0 -> 1安全调用#
function safeApply(fn, thisArg, args) { if (typeof fn !== 'function') { throw new TypeError('First argument must be a function') } return Reflect.apply(fn, thisArg, args)}
// 防止原型污染攻击const untrusted = { apply: 'not a function',}
// untrusted.apply(null, []); // 报错safeApply(String.prototype.trim, ' hello ', []) // 'hello'方法对照表#
| Reflect 方法 | 对应操作 |
|---|---|
| Reflect.get(target, prop, receiver) | target.prop / target[prop] |
| Reflect.set(target, prop, value, receiver) | target.prop = value |
| Reflect.has(target, prop) | prop in target |
| Reflect.deleteProperty(target, prop) | delete target.prop |
| Reflect.construct(Target, args) | new Target(…args) |
| Reflect.getPrototypeOf(target) | Object.getPrototypeOf(target) |
| Reflect.setPrototypeOf(target, proto) | Object.setPrototypeOf(target, proto) |
| Reflect.apply(fn, thisArg, args) | fn.apply(thisArg, args) |
| Reflect.defineProperty(target, prop, desc) | Object.defineProperty(target, prop, desc) |
| Reflect.getOwnPropertyDescriptor(target, prop) | Object.getOwnPropertyDescriptor(target, prop) |
| Reflect.isExtensible(target) | Object.isExtensible(target) |
| Reflect.preventExtensions(target) | Object.preventExtensions(target) |
| Reflect.ownKeys(target) | Object.getOwnPropertyNames + Object.getOwnPropertySymbols |
Reflect 提供了统一的对象操作 API,与 Proxy 配合使用是元编程的最佳实践。