js之Reflect 反射
Reflect是一个内置的对象,它提供了拦截JavaScript操作的方法。它不是一个函数对象,因此不可构造。Reflect对象提供了一些静态方法来操作对象,例如等。这些方法可以用于读取、修改或删除对象的属性,并返回相应的结果。在Vue 3中,Reflect也被用作一个修饰符(decorator),用于监听DOM元素上的属性变化并将其反映到Vue实例的数据上。当DOM元素的属性发生变化时,Vue会
1.Reflect是什么
Reflect是一个内置的对象,它提供了拦截JavaScript操作的方法。它不是一个函数对象,因此不可构造。Reflect对象提供了一些静态方法来操作对象,例如Reflect.get
、Reflect.set
、Reflect.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 Name | Object | Reflect |
---|---|---|
defineProperty() | Object.defineProperty() 返回传递给函数的对象。如果未在对象上成功定义属性,则返回TypeError 。 | 如果在对象上定义了属性,则Reflect.defineProperty()返回true ,否则返回false 。 |
defineProperties() | Object.defineProperties() 返回传递给函数的对象。如果未在对象上成功定义属性,则返回TypeError 。 | N/A |
set() | N/A | 如果在对象上成功设置了属性,则Reflect.set()返回true ,否则返回false 。如果目标不是Object ,则抛出TypeError |
get() | N/A | Reflect.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()返回对象本身。如果设置的原型不是Object 或null ,或者被修改的对象的原型不可扩展,则抛出TypeError 。 | 如果在对象上成功设置了原型,则Reflect.setPrototypeOf() 返回 true,否则返回 false(包括原型是否不可扩展)。如果传入的目标不是Object ,或者设置的原型不是Object 或null ,则抛出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/A | Reflect.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 来代替
更多推荐
所有评论(0)