跳到主要内容

3 篇博文 含有标签「reflect」

查看所有标签

· 阅读需 1 分钟

完成这个表达式:add[1][2][3] + 4 => 10

  1. 链式读取 add 对象的属性,并且累加 key

  2. 在隐式类型转换时,要将内部的累加结果作返回

const add = new Proxy(
{ value: 0 },
{
get(target, key, receiver) {
// 对象 => 原始,会先调用对象的 Symbol.toPrimitive 方法
if (key === Symbol.toPrimitive) {
return () => Reflect.get(target, 'value')
}
target.value += Number(key)
// 支持链式读取,所以返回这个 proxy 本身
return receiver
}
}
)

console.log(add[1][2][3] + 4) // 10

· 阅读需 3 分钟

概念

Reflect 提供了调用对象基本操作(内部方法)的接口

  • ECMA-262 官方文档对于对象的 Internal Methods 有详细描述,比如 [[Get]][[Set]][[OwnPropertyKeys]][[GetPrototypeOf]]

  • MDN 文档对于内部方法的映射,在 Proxy 文档中也有详细说明

也就是说,对象语法层面的操作(对象声明,属性定义与读取等)最终都是通过调用内部方法来完成的

而 Reflect 允许我们直接调用这些内部方法

const obj = {}
obj.a = 1
// 相当于
Reflect.set(obj, 'a', 1)

Why Reflect

当我们使用某个语法或某个 api 来「间接」操作对象时,与直接调用内部方法还是有区别的

也就是说,除了直接调用内部方法以外,这个语法或 api 还会进行一些额外的操作

const obj = {
a: 1,
b: 2,
get c(){
return this.a + this.b
}
}

当我们要用 obj.c 读取属性时,返回了 3,符合我们的预期

但实际上,内部方法 [[Get]] 是需要额外接收 resolver 用来确定 this 指向的

这就说明 obj.c 在读取属性的过程中,一定不只是直接调用内部方法 [[Get]],而是先把 obj 定义为 this,再调用内部方法 [[Get]](obj, 'c', obj),从而获取到 c 属性的值

应用

  • 配合 Proxy 使用

封装代理对象,需要读取某个属性时,this 应该指向代理对象而不是原始对象

const proxy = new Proxy(obj, {
get(target, key) {
console.log('read', key)
// return target[key] // read c
return Reflect.get(target, key, proxy) // read c read a read b
}
})

proxy.c
  • 读取对象的全部属性名

使用 Object.keys(obj)

虽然一开始调用了内部方法 [[OwnPropertyKeys]] 获取到了对象全部的 keys,但紧接着对于不可枚举或 Symbol 类型的属性进行了排除

因此更严谨的获取对象全部自有属性名包括 Symbol 的方法就是直接调用内部方法 Reflect.ownKeys(obj)

· 阅读需 4 分钟

判断属性是否存在

获取/读取/遍历对象的属性主要有以下几种方式:

  1. Object.keys(obj)

获取对象上全部自有的可枚举的字符串属性

自有:相对与原型属性,可以通过 obj.hasOwnProperty('a') 方法检查

可枚举:可枚举的属性,其enumerable 描述符为 true,可以通过 Object.getOwnPropertyDescriptor(obj, 'a') 查看某个属性的描述符

  1. for in

获取对象上全部可枚举的字符串属性(包括原型链上的可枚举属性),与 Object.keys 的区别就在于原型链上的可枚举属性也会被遍历出来

  1. in 关键字

判断字符串属性是否在对象及对象的原型链上,即不会受到自有以及可枚举的限制

  1. Object.getOwnPropertySymbols(obj)

获取对象上全部的 Symbol 属性

  1. Reflect.ownKeys(obj)

返回对象的所有自有属性


判断对象是否为空

常用方法:

  • JSON 序列化:JSON.stringify(obj) === '{}'
    • But: 只能序列化字符串类型且可枚举的键,忽略 Symbol 等其他类型或不可枚举的键
  • 遍历:for in | Object.keys(obj).length === 0
    • But: 同样,无法遍历不可枚举且非字符串的属性

更严谨的方法:

Reflect.ownKeys(obj).length === 0

Reflect 是 ES6(ECMAScript 2015)引入的一个内置的对象,它提供了拦截 JavaScript 操作的方法。

Reflect.ownKeys 方法用来返回一个由目标对象自身的所有键(包括字符串键、Symbol 键)组成的数组。这个方法实际上是在底层提供了一个更完整的方式来获取对象自身的所有属性名称(即使是不可枚举的属性)和 Symbol 键值。

因为它设计用来执行默认操作,而无论是字符串还是 Symbol 类型的键,都是对象自身属性的一部分。在语言规范中,对象属性的集合被视为属性的名字和它们对应的属性描述符的集合。Reflect.ownKeys 正是直接提供了这个集合的视图。

详见 Reflect 一文

  • 过分一点的需求:确定对象的原型链上也没有属性
  1. 使用Object.getPrototypeOf
function readPrototypeProperties(object) {
let proto = Object.getPrototypeOf(object);
while (proto) {
console.log(Object.getOwnPropertyNames(proto)); // 输出当前原型上的属性
proto = Object.getPrototypeOf(proto);
}
}

readPrototypeProperties(obj);
  1. 使用__proto__属性
let proto = obj.__proto__;
while (proto) {
console.log(Object.getOwnPropertyNames(proto)); // 输出当前原型上的属性
proto = proto.__proto__;
}