# ES6 Reflect

# 前言

  此文总结了Reflect对象的部分语法,对比了与Object方法的差异性,希望对你有用。

# 语法

  ;ReflectMath类似,都是JavaScript内置对象,提供了工具方法。

typeof Reflect // object

# get

  ;Reflect.get(target, property, receiver) (opens new window) 用于读取对象属性,其中target为目标对象,property为属性名称。

var object = { x: 1 }

Reflect.get(object, 'x') // 1

  读取目标对象的访问器属性时,访问器getterthis上下文就是参数receiver。未指定参数receiver,默认为目标对象。

var object = {
  y: 1,
  get x() {
    return this
  },
}
var receiver = {}

Reflect.get(object, 'x') === object // true
Reflect.get(object, 'x', receiver) === receiver // true

# set

  ;Reflect.set(target, property, value, receiver) (opens new window) 用于设置对象属性,其中target为目标对象,property为属性名称,value为属性值。

var object = { x: 1 }

Reflect.set(object, 'x', 2)

object // {x: 2}

  类似的,访问器setterthis上下文就是参数receiver,默认为目标对象。

var object = {
  y: 1,
  set x(v) {
    this.y = v
  },
}
var receiver = {}

Reflect.set(object, 'x', 2)
object // {y: 2}

Reflect.set(object, 'x', 3, receiver)
receiver // {y: 3}

# has

  ;Reflect.has(object, property) (opens new window) 判断对象是否有此属性,本质上与in操作符功能相同。

var object = { x: undefined }

Reflect.has(object, 'x') // true

# deleteProperty

  ;Reflect.deleteProperty(target, property) (opens new window) 用于删除对象属性,本质上与delete操作符功能相同。

var object = { x: 1 }

Reflect.deleteProperty(object, 'x')

object // {}

# ownKeys

  ;Reflect.ownKeys(target) (opens new window) 返回对象自身所有属性,等价于Object.getOwnPropertyNames(target)Object.getOwnPropertySymbols(target)之和。

var object = { x: 1, [Symbol('y')]: 2 }

Object.defineProperty(object, 'z', {
  value: 3,
  enumerable: false,
})

Reflect.ownKeys(object) // ['x', 'z', Symbol(y)]

# getOwnPropertyDescriptor

  ;Reflect.getOwnPropertyDescriptor(target, property) (opens new window) 用于获取对象属性描述符。

var object = { x: 1 }

Reflect.getOwnPropertyDescriptor(object, 'x')
// {
//   configurable: true,
//   enumerable: true,
//   value: 1,
//   writable: true,
// }

  与Object.getOwnPropertyDescriptor不同之处在于,target为非对象时,Object版本静默失败并返回undefined。而Reflect版本则抛出错误,提示开发者注意参数类型。

Object.getOwnPropertyDescriptor(1, 'x')
// undefined

Reflect.getOwnPropertyDescriptor(1, 'x')
// Uncaught TypeError: Reflect.getOwnPropertyDescriptor called on non-object

# defineProperty

  ;Reflect.defineProperty(target, property, descriptor) (opens new window) 用于定义对象属性,其中target为目标对象,value为属性名称,descriptor为属性描述符。

var object = {}

Reflect.defineProperty(object, 'x', { value: 1 })

Reflect.getOwnPropertyDescriptor(object, 'x')
// {
//   configurable: false,
//   enumerable: false,
//   value: 1,
//   writable: false,
// }

  若属性定义失败,Object版本将抛出错误,而Reflect版本将返回false

var object = Object.freeze({})

Reflect.defineProperty(object, 'x', { value: 1 })
// false

Object.defineProperty(object, 'x', { value: 1 })
// Uncaught TypeError: Cannot define property x, object is not extensible

# preventExtensions

  ;Reflect.preventExtensions(target) (opens new window) 阻止对象拓展。

var object = {}

Reflect.preventExtensions(object)

Reflect.isExtensible(object) // false

  类似的,target为非对象时,Object版本静默失败,而Reflect版本将抛出错误。

Object.preventExtensions(1)
// 1

Reflect.preventExtensions(1)
// Uncaught TypeError: Reflect.preventExtensions called on non-object

# isExtensible

  ;Reflect.isExtensible(target) (opens new window) 判断对象是否可拓展。

var object = {}

Reflect.preventExtensions(object)

Reflect.isExtensible(object) // false

  若参数为非对象,Reflect版本将抛出错误,而Object版本则静默失败并返回false。不合理之处在于参数为非对象,讨论是否可拓展并没有任何意义。

Reflect.isExtensible(1)
// Uncaught TypeError: Reflect.isExtensible called on non-object

Object.isExtensible(1)
// false

# getPrototypeOf

  ;Reflect.getPrototypeOf(target) (opens new window) 获取对象原型。

Reflect.getPrototypeOf({}) === Object.prototype // true

  参数target为非对象时,Object版本存在类型转换,而Reflect版本将抛出错误。

Object.getPrototypeOf(1) === Number.prototype
// true

Reflect.getPrototypeOf(1)
// Uncaught TypeError: Reflect.getPrototypeOf called on non-object

# setPrototypeOf

  ;Reflect.setPrototypeOf(target, prototype) (opens new window) 用于设置原型,返回值为布尔值。

var object = {}

Reflect.setPrototypeOf(object, null)

Reflect.getPrototypeOf(object) // null

  类似的,参数target为非对象时,Object版本静默失败并返回target,而Reflect版本将抛出错误。

Object.setPrototypeOf(1, null)
// 1

Reflect.setPrototypeOf(1, null)
// Uncaught TypeError: Reflect.setPrototypeOf called on non-object

# apply

  ;Reflect.apply(target, thisArg, args) (opens new window) 用于调用函数,其中target为目标函数,thisArg为函数被调用时的上下文对象thisargs为函数参数。

function fn(x, y) {
  return this.v + x + y
}

Reflect.apply(fn, { v: 1 }, [2, 3]) // 6

  若函数apply属性被占用,运行apply方法绑定this将抛出错误。

function fn(x, y) { return x + y }

fn.apply = 1

fn.apply(null, [3, 4])
// Uncaught TypeError: fn.apply is not a function

  替换为原型apply方法可规避,但语义不明显。

Function.prototype.apply.call(fn, null, [3, 4]) // 7

  而Reflect.apply执行方式则更简洁清晰。

Reflect.apply(fn, null, [3, 4]) // 7

# construct

  ;Reflect.construct(target, args, newTarget) (opens new window)new操作符行为类似,其中target为构造函数,args为函数参数。

function F(x, y) {
  this.x = x
  this.y = y
}

Reflect.construct(F, [1, 2]) // F {x: 1, y: 2}

  参数newTarget有两个用处,第一个是指定新对象的原型为newTarget的原型对象。

function F() {}
function NT() {}

var object = Reflect.construct(F, [], NT)

Reflect.getPrototypeOf(object) === NT.prototype // true

  除此之外,原构造函数内部new.target会被指向newTarget函数。

function F() {
  console.log(new.target === NT) // true
}
function NT() {}

Reflect.construct(F, [], NT)

# 小结

  ;Reflect对象的设计目的主要包括。

  • 语言内部的元编程行为,统一移动至Reflect,未来新的元编程行为将只添加到Reflect
  • Object部分函数的行为不合理,在Reflect版本进行修正
  • 符合函数式编程,语义更清晰
  • ProxyReflect结合在代理同时还能保持默认行为

# 🎉 写在最后

🍻伙伴们,如果你已经看到了这里,觉得这篇文章有帮助到你的话不妨点赞👍或 Star (opens new window) ✨支持一下哦!

手动码字,如有错误,欢迎在评论区指正💬~

你的支持就是我更新的最大动力💪~

GitHub (opens new window) / Gitee (opens new window)GitHub Pages (opens new window)掘金 (opens new window)CSDN (opens new window) 同步更新,欢迎关注😉~

最后更新时间: 12/4/2022, 4:32:17 PM