js之Reflect 反射

1.Reflect是什么

Reflect是一个内置的对象,它提供了拦截JavaScript操作的方法。它不是一个函数对象,因此不可构造。Reflect对象提供了一些静态方法来操作对象,例如Reflect.getReflect.setReflect.deleteProperty等。这些方法可以用于读取、修改或删除对象的属性,并返回相应的结果。

在Vue 3中,Reflect也被用作一个修饰符(decorator),用于监听DOM元素上的属性变化并将其反映到Vue实例的数据上。当DOM元素的属性发生变化时,Vue会捕获这些变化,并更新相应的数据,从而保持数据和视图的同步。

总的来说,Reflect在Vue 3中扮演着重要的角色,它提供了一种机制来拦截和操作JavaScript对象,从而实现响应式数据绑定和视图更新。

2.Reflect对象的设计目的

 1 .将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。也就是说,从Reflect对象上可以拿到语言内部的方法。
 2 .修改某些Object方法的返回结果,让其变得更合理。
 3 .让Object操作都变成函数行为
 4 .Reflect对象的方法与Proxy对象的方法一一对应  

3. Reflect 13 个静态方法  

Reflect.get(target, name, receiver) 查找并返回target对象的name属性,receiver绑定this
Reflect.set(target, name, value, receiver) 设置target对象的name属性等于value
Reflect.has(obj, name) 方法对应name in obj里面的in运算符
Reflect.deleteProperty(obj, name) 方法等同于delete obj[name],用于删除对象的属性。
Reflect.construct(target, args) 等同于new target(...args),调用构造函数的方法。
Reflect.getPrototypeOf(obj) 读取对象的__proto__属性,对应Object.getPrototypeOf
Reflect.setPrototypeOf(obj, newProto) 设置目标对象的原型 对应Object.setPrototypeOf
Reflect.apply(func, thisArg, args) 等同于Function.prototype.apply.call(func, thisArg, args)
Reflect.defineProperty(target, propertyKey, attributes) 等同于Object.defineProperty
Reflect.getOwnPropertyDescriptor(target, propertyKey) 等同于Object.getOwnPropertyDescriptor
Reflect.isExtensible (target) 对应Object.isExtensible 表示当前对象是否可扩展。
Reflect.preventExtensions(target) 对应Object.preventExtensions 让一个对象变为不可扩展
Reflect.ownKeys (target) 返回对象的所有属性,可以返回Symbol类型

 

4.静态方法

  • Reflect.apply(target, thisArg, args)

对一个函数进行调用操作,同时可以传入一个数组作为调用参数。

 
var obj1 = {};
Reflect.apply(Math.floor, obj1, [1.88]) // 1;
  • Reflect.construct(target, args)

对构造函数进行 new 操作,相当于执行 new target(...args)。

 
const obj2 = Reflect.construct(Date, [2021, 3, 1]);
  • Reflect.get(target, name, receiver)

获取对象身上某个属性的值,类似于 target[name]。如果没有该属性,则返回undefined。

 
var obj3 = { x: 1, y: 2 }; 
Reflect.get(obj3, "x"); // 1
  • Reflect.set(target, name, value, receiver) 将值分配给属性的函数。返回一个Boolean,如果更新成功,则返回true。
 
var obj4 = {}; 
Reflect.set(obj4, "prop", "value"); // true
  • Reflect.defineProperty(target, name, desc)

Reflect.defineProperty方法基本等同于Object.defineProperty,直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,不同的是,Object.defineProperty返回此对象。而Reflect.defineProperty会返回布尔值.

 
const obj5 = {}; 
Reflect.defineProperty(obj5, 'property', { 
    value: 666, 
    writable: false 
}); // true
  • Reflect.deleteProperty(target, name)

作为函数的delete操作符,相当于执行 delete target[name]。

 
var obj6 = { x: 1, y: 2 }; 
Reflect.deleteProperty(obj6, "x"); // true 
obj; // { y: 2 }
  • Reflect.has(target, name)

判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同。

 
const obj7 = {x: 0};
Reflect.has(obj7, "x"); // true
  • Reflect.ownKeys(target)

返回一个包含所有自身属性(不包含继承属性)的数组。(类似于 Object.keys(), 但不会受enumerable影响, Object.keys返回所有可枚举属性的字符串数组).

 
const obj8 = {z: 3, y: 2, x: 1};
Reflect.ownKeys(obj8); // [ "z", "y", "x" ]
  • Reflect.isExtensible(target)

判断一个对象是否是可扩展的(是否可以在它上面添加新的属性),类似于 Object.isExtensible()。返回表示给定对象是否可扩展的一个Boolean 。(Object.seal 或 Object.freeze 方法都可以标记一个对象为不可扩展。)

 
var obj9 = {}; 
Reflect.isExtensible(obj9); // true
  • Reflect.preventExtensions(target)

让一个对象变的不可扩展,也就是永远不能再添加新的属性。

 
var obj10 = {}; 
Reflect.isExtensible(obj10); // true 
Reflect.preventExtensions(obj10); 
Reflect.isExtensible(obj10); // false
  • Reflect.getOwnPropertyDescriptor(target, name)

如果对象中存在该属性,如果指定的属性存在于对象上,则返回其属性描述符对象(property descriptor),否则返回 undefined。类似于 Object.getOwnPropertyDescriptor()。

 
const obj11 = {x: "hello"};
Reflect.getOwnPropertyDescriptor(obj11, "x");
// {value: "hello", writable: true, enumerable: true, configurable: true}

Reflect.getPrototypeOf(target)

返回指定对象的原型.类似于 Object.getOwnPropertyDescriptor()。

 
var obj12 = {};
Reflect.getPrototypeOf(obj12); // 等同于Object.prototype
  • Reflect.setPrototypeOf(target, prototype)

设置对象原型的函数. 返回一个 Boolean, 如果更新成功,则返回true。如果 target 不是 Object ,或 prototype 既不是对象也不是 null,抛出一个 TypeError 异常。

 
var obj13 = {}; 
Reflect.setPrototypeOf(obj13, null); // true

5.Object 和 Reflect API 上可用方法之间的差异

如果 API 中不存在某种方法,则将其标记为 N/A。

Method NameObjectReflect
defineProperty()Object.defineProperty() 返回传递给函数的对象。如果未在对象上成功定义属性,则返回TypeError如果在对象上定义了属性,则Reflect.defineProperty()返回true,否则返回false
defineProperties()Object.defineProperties() 返回传递给函数的对象。如果未在对象上成功定义属性,则返回TypeErrorN/A
set()N/A如果在对象上成功设置了属性,则Reflect.set()返回true,否则返回false。如果目标不是Object,则抛出TypeError
get()N/AReflect.get()返回属性的值。如果目标不是Object,则抛出TypeError
deleteProperty()N/A如果属性从对象中删除,则Reflect.deleteProperty()返回true,否则返回false
getOwnPropertyDescriptor()如果传入的对象参数上存在Object.getOwnPropertyDescriptor() ,则会返回给定属性的属性描述符,如果不存在,则返回undefined如果给定属性存在于对象上,则Reflect.getOwnPropertyDescriptor() 返回给定属性的属性描述符。如果不存在则返回undefined,如果传入除对象(原始值)以外的任何东西作为第一个参数,则返回TypeError
getOwnPropertyDescriptors()Object.getOwnPropertyDescriptors() 返回一个对象,其中包含每个传入对象的属性描述符。如果传入的对象没有拥有的属性描述符,则返回一个空对象。N/A
getPrototypeOf()Object.getPrototypeOf()返回给定对象的原型。如果没有继承的原型,则返回null。在 ES5 中为非对象抛出TypeError,但在 ES2015 中强制为非对象。Reflect.getPrototypeOf()返回给定对象的原型。如果没有继承的原型,则返回 null,并为非对象抛出TypeError
setPrototypeOf()如果对象的原型设置成功,则Object.setPrototypeOf()返回对象本身。如果设置的原型不是Objectnull,或者被修改的对象的原型不可扩展,则抛出TypeError如果在对象上成功设置了原型,则Reflect.setPrototypeOf() 返回 true,否则返回 false(包括原型是否不可扩展)。如果传入的目标不是Object,或者设置的原型不是Objectnull,则抛出TypeError
isExtensible()如果对象是可扩展的,则 Object.isExtensible()返回 true,否则返回 false。如果第一个参数不是对象(原始值),则在 ES5 中抛出TypeError。在 ES2015 中,它将被强制为不可扩展的普通对象并返回false如果对象是可扩展的,则Reflect.isExtensible() 返回true,否则返回false。如果第一个参数不是对象(原始值),则抛出TypeError
preventExtensions()Object.preventExtensions() 返回被设为不可扩展的对象。如果参数不是对象(原始值),则在 ES5 中抛出TypeError。在 ES2015 中,参数如为不可扩展的普通对象,然后返回对象本身。returns true if the object has been made non-extensible, and false if it has not. Throws a TypeError if the argument is not an object (a primitive).如果对象已变得不可扩展,则Reflect.preventExtensions() 返回true,否则返回false。如果参数不是对象(原始值),则抛出TypeError
keys()Object.keys()返回一个字符串数组,该字符串映射到目标对象自己的(可枚举)属性键。如果目标不是对象,则在 ES5 中抛出TypeError,但将非对象目标强制为 ES2015 中的对象N/A
ownKeys()N/AReflect.ownKeys()返回一个属性名称数组,该属性名称映射到目标对象自己的属性键。如果目标不是Object,则抛出TypeError

6.它可以做什么? 

使用Reflect可以实现诸如 属性的赋值与取值、调用普通函数、调用构造函数、判断属性是否存在与对象中 等等功能

7.这些功能已经存在了,为什么还需要用Reflect实现一次? 

有一个理念,在ES5就被提出:减少魔法、让代码更加纯粹

这种理念很大程度上是受到函数式编程的影响

ES6进一步贯彻了这种理念,它认为,对属性内存的控制、原型链的修改、函数的调用等等,这些都属于底层实现,属于一种魔法,因此,需要将它们提取出来,形成一个正常的API,并高度聚合到某个对象中,于是,就造就了Reflect对象

因此,你可以看到Reflect对象中有很多的API都可以使用过去的某种语法或其他API实现。

 8.Reflect API 的好处

状态标记

或许 Object 上也有相同的方法,但是通常 Object 会因为报错而阻塞程序,而 Reflect 返回操作的 boolean 状态值,表示操作成功与否,返回 true 表示操作成功

常见的 Reflect 提供状态标记的 api 有

  • Reflect.defineProperty
  • Reflect.preventExtensions
  • Reflect.setPrototypeOf
  • Reflect.deleteProperty
const obj = {};

try {
  Object.defineProperty(obj, "key", "xxx");
} catch (err) {
  log("err: ", err);
}

// 可以使用 状态标记来 重构上述代码
if (!Reflect.defineProperty(obj, "key", "xx")) {
  log("obj define key failed");
}

用一等函数替代操作符

  • Reflect.get 替代 obj.key / obj["key"]
  • Relfect.set 替代 obj.key = value / obj["key"] = value;
  • Reflect.has 替代 in 或者 with
  • Reflect.deleteProperty 替代 delete 操作符
  • Reflect.construct 替代 new 操作符
const person = {
  name: "jakequc",
  age: 23,
};

// 替代 person.name / person["name"]
log(Reflect.get(person, "name")); // log: jakequc

// 替代 person.name = "new_name" or person["name"] = "new_name"
Reflect.set(person, "name", "new_name");

log(Reflect.get(person, "name")); // log: new_name

// 替代 ( "name" in person )或者 with()
log(Reflect.has(person, "name")); // log: true

// 替代 delete 操作符
log(Reflect.deleteProperty(person, "name")); // log: true 表示删除成功
log(Reflect.has(person, "name")); // log: fasle, 因为上一行已经删除了

// 替代 new 操作符
const arr = Reflect.construct(Array, [1, 2, 3, 4]);
log(arr); // log: [ 1, 2, 3, 4 ]

安全地应用函数

可能某些方法或属性自定义的覆盖了内置的;比如 apply 方法调用函数时,被调用的函数可能也定义了自己的 apply 属性(虽然很小),为了避免这个问题,可以使用 Function.prototype.apply.call 外,还可以使用 Reflect.apply 来代替

 

 

 

 

  • 14
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值