Skip to content

Reflect 元编程

Reflect 是 ES6 新增的内置对象,提供了拦截 JavaScript 操作的方法,与 Proxy handler 方法一一对应。

为什么需要 Reflect#

在 Reflect 之前,对象的元操作分散在 Object、Function、操作符等多处:

// 检查属性
'name' in obj
Object.prototype.hasOwnProperty.call(obj, 'name')
// 删除属性
delete obj.name
// 获取属性
obj.name
obj['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 返回 false
if (!Reflect.defineProperty(Object.freeze({}), 'name', { value: 1 })) {
console.log('失败')
}

2. 函数式操作#

// 操作符形式
'name' in obj
delete 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 的 this
const receiver = { name: '李四' }
Reflect.get(obj, 'fullName', receiver) // '李四先生'

Reflect.set()#

const obj = {
_name: '张三',
set name(value) {
this._name = value
},
}
Reflect.set(obj, 'name', '李四') // true
console.log(obj._name) // '李四'
// 使用 receiver
const receiver = { _name: '' }
Reflect.set(obj, 'name', '王五', receiver)
console.log(receiver._name) // '王五'
console.log(obj._name) // '李四'(未改变)

Reflect.has()#

const obj = { name: '张三' }
Reflect.has(obj, 'name') // true
Reflect.has(obj, 'age') // false
// 等价于 in 操作符
'name' in obj // true

Reflect.deleteProperty()#

const obj = { name: '张三', age: 25 }
Reflect.deleteProperty(obj, 'age') // true
console.log(obj) // { name: '张三' }
// 删除不存在的属性也返回 true
Reflect.deleteProperty(obj, 'notExist') // true
// 删除不可配置的属性返回 false
Object.defineProperty(obj, 'id', { value: 1, configurable: false })
Reflect.deleteProperty(obj, 'id') // false

Reflect.construct()#

function Person(name) {
this.name = name
}
// 等价于 new Person('张三')
const person = Reflect.construct(Person, ['张三'])
console.log(person.name) // '张三'
// 可以指定 new.target
class Animal {}
class Dog extends Animal {}
const instance = Reflect.construct(Animal, [], Dog)
console.log(instance instanceof Dog) // true

Reflect.getPrototypeOf()#

const obj = {}
Reflect.getPrototypeOf(obj) === Object.prototype // true
// 与 Object.getPrototypeOf 的区别
// Object.getPrototypeOf('string'); // 强制转换为对象
// Reflect.getPrototypeOf('string'); // TypeError

Reflect.setPrototypeOf()#

const obj = {}
const proto = {
greet() {
return 'Hello'
},
}
Reflect.setPrototypeOf(obj, proto) // true
obj.greet() // 'Hello'
// 设置 null
Reflect.setPrototypeOf(obj, null) // true
// 对冻结对象返回 false
const frozen = Object.freeze({})
Reflect.setPrototypeOf(frozen, {}) // false

Reflect.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) // true
console.log(obj.name) // '张三'
// 失败时返回 false
const frozen = Object.freeze({})
Reflect.defineProperty(frozen, 'x', { value: 1 }) // false

Reflect.getOwnPropertyDescriptor()#

const obj = { name: '张三' }
Reflect.getOwnPropertyDescriptor(obj, 'name')
// {
// value: '张三',
// writable: true,
// enumerable: true,
// configurable: true
// }
// 不存在的属性返回 undefined
Reflect.getOwnPropertyDescriptor(obj, 'age') // undefined

Reflect.isExtensible()#

const obj = {}
Reflect.isExtensible(obj) // true
Object.preventExtensions(obj)
Reflect.isExtensible(obj) // false

Reflect.preventExtensions()#

const obj = {}
Reflect.preventExtensions(obj) // true
Reflect.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)
// 不使用 receiver
const proxy1 = new Proxy(parent, {
get(target, property) {
return Reflect.get(target, property)
},
})
Object.setPrototypeOf(child, proxy1)
child.value // 'parent'(错误!应该是 'child')
// 使用 receiver
const 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 // 读取: count
data.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 配合使用是元编程的最佳实践。