Skip to content

Reflect

Reflect对象设计目的如下

  1. Object对象属于语言内部的方法(),放到Reflect对象上。现阶段,某些方法同时在ObjectReflect对象上部署,未来的新方法将只部署在Reflect对象上。Reflect对象上可以拿到语言内部的方法

比如Object.defineProperty

  1. 修改某些Object方法的返回结果,让其变得更合理。

比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false

js
// 老写法
try {
  Object.defineProperty(target, property, attributes)
  // success
} catch (e) {
  // failure
}

// 新写法
if (Reflect.defineProperty(target, property, attributes)) {
  // success
} else {
  // failure
}
  1. Object操作都变成函数行为,某些Object操作是命令式。比如name in objdelete obj[name]
js
// 老写法
'assign' in Object // true

// 新写法
Reflect.has(Object, 'assign') // true
  1. Reflect对象的方法与Proxy对象的方法一一对应。不管Proxy怎么修改默认行为,你总可以在Reflect上获取默认行为。

Proxy方法拦截target对象属性赋值行为。采用Reflect.set方法将值赋给对象属性,确保默认行为,再部署额外功能

js
Proxy(target, {
  set: function (target, name, value, receiver) {
    var success = Reflect.set(target, name, value, receiver)
    if (success) {
      console.log('property ' + name + ' on ' + target + ' set to ' + value)
    }
    return success
  }
})
  1. Reflect对象,使操作更易读
js
// 老写法
Function.prototype.apply.call(Math.floor, undefined, [1.75]) // 1

// 新写法
Reflect.apply(Math.floor, undefined, [1.75]) // 1

静态方法

Reflect对象一共有 13 个静态方法,与proxy一一对应

Reflect.get

Reflect.get(target, name, receiver):查找并返回target对象的name属性。没有属性,返回undefined

注意事项

  1. 属性部署了读取函数(getter),读取函数this绑定receiver
js
var myObject = {
  foo: 1,
  bar: 2,
  get baz() {
    return this.foo + this.bar
  }
}

var myReceiverObject = {
  foo: 4,
  bar: 4
}

Reflect.get(myObject, 'baz', myReceiverObject) // 8
  1. 第一个参数不是对象,Reflect.get方法报错
js
Reflect.get(1, 'foo') // 报错
Reflect.get(false, 'foo') // 报错

Reflect.set

Reflect.set(target, name, value, receiver):设置target对象的name属性等于value

注意事项

  1. 属性部署了赋值函数(setter),赋值函数this绑定receiver
js
var myObject = {
  foo: 4,
  set bar(value) {
    return (this.foo = value)
  }
}

var myReceiverObject = {
  foo: 0
}

Reflect.set(myObject, 'bar', 1, myReceiverObject)
myObject.foo // 4
myReceiverObject.foo // 1
  1. 第一个参数不是对象,Reflect.get方法报错

  2. Proxy 和 Reflect 联合使用,前者拦截赋值,后者完成赋值默认行为

  • receiver默认指向当前Proxy实例(obj
  • 传入receiver,赋值函数会将属性赋值到recevier上(也就是obj)。导致触发defineProperty拦截。不传入,不会触发。
js
let p = {
  a: 'a'
}

let handler = {
  set(target, key, value, receiver) {
    console.log('set')
    Reflect.set(target, key, value, receiver)
  },
  defineProperty(target, key, attribute) {
    console.log('defineProperty')
    Reflect.defineProperty(target, key, attribute)
  }
}

let obj = new Proxy(p, handler)
obj.a = 'A'
// set
// defineProperty

Reflect.has

Reflect.has(target, name):对应name in obj里面的in运算符

js
// 旧写法
'foo' in myObject // true
// 新写法
Reflect.has(myObject, 'foo') // true

第一参数不是对象,报错

Relect.deleteProperty

Reflect.deleteProperty(target, name):对应delete obj[name],用于删除对象的属性。返回一个布尔值,删除失败,属性依然存在,返回false

js
// 旧写法
delete myObj.foo
// 新写法
Reflect.deleteProperty(myObj, 'foo')

第一参数不是对象,报错

Reflect.construct

Reflect.construct(target, args):对应new target(...args),一种不使用new,来调用构造函数的方法

js
// new 的写法
const instance = new Greeting('张三')
// Reflect.construct 的写法
const instance = Reflect.construct(Greeting, ['张三'])

第一参数不是函数,报错

Reflect.getPrototypeOf

Reflect.getPrototypeOf(target):对应Object.getPrototypeOf(obj),读取对象的__proto__属性

Object.getPrototypeOf:参数不为对象,转为对象运行

js
// 旧写法
Object.getPrototypeOf(myObj) === FancyThing.prototype
// 新写法
Reflect.getPrototypeOf(myObj) === FancyThing.prototype

参数不为对象,报错

Reflect.setPrototypeOf

Reflect.setPrototypeOf(target, prototype):对应Object.setPrototypeOf(obj, newProto),设置目标对象的原型(prototype),返回布尔值,表示是否设置成功

js
const myObj = {}

// 旧写法
Object.setPrototypeOf(myObj, Array.prototype)

// 新写法
Reflect.setPrototypeOf(myObj, Array.prototype)

myObj.length // 0

注意事项

  1. 无法设置目标对象的原型(比如,目标对象禁止扩展),返回 false
js
Reflect.setPrototypeOf({}, null)
// true
Reflect.setPrototypeOf(Object.freeze({}), null)
// false
  1. 第一参数不是对象,Object.setPrototypeOf会返回第一个参数本身,而Reflect.setPrototypeOf会报错
js
Object.setPrototypeOf(1, {})
// 1

Reflect.setPrototypeOf(1, {})
// TypeError: Reflect.setPrototypeOf called on non-object
  1. undefinednullObject.setPrototypeOfReflect.setPrototypeOf均会报错(第一参数)
js
Object.setPrototypeOf(null, {})
// TypeError: Object.setPrototypeOf called on null or undefined

Reflect.setPrototypeOf(null, {})
// TypeError: Reflect.setPrototypeOf called on non-object

Reflect.apply

Reflect.apply(target, thisArg, args):对应Function.prototype.apply.call(func, thisArg, args),用于绑定this对象执行给定函数

绑定函数this对象

js
fn.apply(obj, args)
// 函数本身定义了apply
Function.prototype.apply.call(fn, obj, args)

Reflect.apply(fn, obj, args)
js
const ages = [11, 33, 12, 54, 18, 96]
// 旧写法
const youngest = Math.min.apply(Math, ages)
const oldest = Math.max.apply(Math, ages)
const type = Object.prototype.toString.call(youngest) // "[object Number]"
// 新写法
const youngest = Reflect.apply(Math.min, Math, ages)
const oldest = Reflect.apply(Math.max, Math, ages)
const type = Reflect.apply(Object.prototype.toString, youngest, [])

Reflect.defineProperty

Reflect.defineProperty(target, name, desc):对应Object.defineProperty,用来为对象定义属性

js
// 旧写法
Object.defineProperty(MyDate, 'now', {
  value: () => Date.now()
})

// 新写法
Reflect.defineProperty(MyDate, 'now', {
  value: () => Date.now()
})

搭配 Proxy.defineProperty 使用

js
const p = new Proxy(
  {},
  {
    defineProperty(target, prop, descriptor) {
      console.log(descriptor)
      return Reflect.defineProperty(target, prop, descriptor)
    }
  }
)

p.foo = 'bar'
// {value: "bar", writable: true, enumerable: true, configurable: true}

p.foo // "bar"

第一参数不是对象会报错

Reflect.getOwnPropertyDescriptor

Reflect.getOwnPropertyDescriptor(target, name):对应Object.getOwnPropertyDescriptor,用于得到指定属性的描述对象

js
var myObject = {}
Object.defineProperty(myObject, 'hidden', {
  value: true,
  enumerable: false
})

// 旧写法
var theDescriptor = Object.getOwnPropertyDescriptor(myObject, 'hidden')
// 新写法
var theDescriptor = Reflect.getOwnPropertyDescriptor(myObject, 'hidden')

Object.getOwnPropertyDescriptor 和 Reflect.getOwnPropertyDescriptor 区别

Object 第一参数不是对象不报错,Reflect 第一参数不是对象,会报错。

Reflect.isExtensible

Reflect.isExtensible(target):对应Object.isExtensible,返回一个布尔值,表示当前对象是否可扩展

js
// 旧写法
Object.isExtensible(myObject) // true
// 新写法
Reflect.isExtensible(myObject) // true

Object.isExtensible 和 Reflect.isExtensible 区别

Object.isExtensible参数不为对象,返回falseReflect.isExtensible直接报错

Reflect.preventExtensions

Reflect.preventExtensions(target):对应Object.preventExtensions方法,让对象变为不可扩展。它返回一个布尔值,表示是否操作成功

区别

  • Object.preventExtensions参数不是对象,ES5报错,ES6返回传入参数
  • Reflect.preventExtensions会报错
js
// 旧写法
Object.preventExtensions(myObject) // Object {}
// 新写法
Reflect.preventExtensions(myObject) // true

Reflect.ownKeys

Reflect.ownKeys(target):返回对象的所有属性。等同Object.getOwnPropertyNamesObject.getOwnPropertySymbols之和

第一参数不是对象,报错

实现观察者模式

观察者模式(Observer mode):指的是函数自动观察数据对象,一旦对象有变化,函数就会自动执行

js
const person = observable({
  name: '张三',
  age: 20
})
function print() {
  console.log(`${person.name}, ${person.age}`)
}

observe(print) // 将print加入观察者函数队列
person.name = '李四'
// 李四, 20
js
const queuedObservers = new Set()

const observe = (fn) => queuedObservers.add(fn) // 观察者函数
const observable = (obj) => new Proxy(obj, { set }) // 观察者对象

function set(target, key, value, receiver) {
  const result = Reflect.set(target, key, value, receiver)
  queuedObservers.forEach((observer) => observer()) // 调用队列中函数
  return result
}

相关链接

[-] Reflect