Reflect & Proxy

Reflect和Proxy 🥳

1. Proxy 🕶️

1.1 语法🤡

我们可以使用 Proxy() 构造器来创建一个新的 Proxy 对象。构造器接收两个主要参数:

  • target 被代理的对象
  • handler 被代理对象上的自定义行为

像这样:

1
new Proxy(target, handler)

一个空的 handler 参数将会创建一个与被代理对象行为几乎完全相同的代理对象。通过在 handler 对象上定义一组处理函数,你可以自定义被代理对象的一些特定行为。例如,通过定义 get() 你就可以自定义被代理对象的属性访问器。

1.2 handler们😱

名字 功能
handler.apply() 函数调用劫持
handler.construct() new 操作符劫持
handler.defineProperty() Object.defineProperty调用劫持
handler.deleteProperty() delete 操作符劫持
handler.get() 获取属性值劫持
handler.getOwnPropertyDescriptor() Object.getOwnPropertyDescriptor 调用劫持
handler.getPrototypeOf() Object.getPrototypeOf调用劫持
handler.has() in操作符劫持
handler.isExtensible() Object.isExtensible调用劫持
handler.ownKeys() Object.getOwnPropertyNamesObject.getOwnPropertySymbols调用劫持。
handler.preventExtensions() Object.preventExtensions调用劫持
handler.set() 设置属性值劫持
handler.setPrototypeOf() Object.setPrototypeOf调用劫持

因为是对对象功能的劫持,所以功能差不多与Object的方法名一一对应🫨

1.3 示例💩

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const cs = {
Ak47: 2700,
AWP: 4750,
amor: 650
};

const valorantHandler = {
get: function (target, prop, receiver) {
if (prop === "AWP") {
return 4700;
}
return Reflect.get(...arguments);
},
};

const proxy = new Proxy(cs, valorantHandler);
Object.keys(proxy).map(key => {
console.log(proxy[key])
})

🤪猜猜会输出什么嘞

🤪打瓦的闭嘴

Proxy第一个参数都是target,指定代理的对象;

第二个参数prop指定代理属性名;

第三个参数receiver用于指定this指向,如果不加上可能会导致this不会正确配置(不太懂,没找到例子🤔)

1.4 有gettersetter了,为什么还要有个proxy嘞🦢

虽然 GetterSetter 提供了对单个属性操作的控制,但它们不能直接应用于整个对象或拦截其他类型的操作。Proxy 提供了对整个对象操作的完全控制,可以拦截和自定义几乎所有对对象的操作。这使得 Proxy 在进行复杂操作或创建高级抽象时非常有用。

还是那段代码,想想看要是想动态加上一个属性m4,不管输入什么最后都打印出0。用getter或者setter怎么写?显然是不可能的,因为gettersetter管不到defineProperties, 但是要换成proxy`就很简单了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const cs = {
Ak47: 2700,
AWP: 4750,
amor: 650
};

const valorantHandler = {
get: function (target, prop, receiver) {
if (prop === "AWP") {
return 4700;
}
return Reflect.get(...arguments);
},
defineProperty(target, prop, descriptor) {
console.log(`Defining property: ${prop}`);
if(prop === 'usp') {
console.log(0)
}
return Reflect.defineProperty(target, prop, descriptor);
}
};

const proxy = new Proxy(cs, valorantHandler);
Object.defineProperty(proxy,"usp", {
value:8848,
enumerable: true
})
Object.keys(proxy).map(key => {
console.log(key, proxy[key])
})

但是proxy相对于原生object处理速度会慢很多,所以也不能大量使用proxy,会严重影响性能👽

2. Reflect🤕

Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与 proxy 的方法相同。Reflect 不是一个函数对象,因此它是不可构造的。

2.1 属性表

名字 功能
Reflect.apply(target, thisArgument, argumentsList) 对一个函数进行调用操作,同时可以传入一个数组作为调用参数。和 Function.prototype.apply() 功能类似。
Reflect.construct(target, argumentsList[, newTarget]) 对构造函数进行 new操作,相当于执行 new target(...args)
Reflect.defineProperty(target, propertyKey, attributes) Object.defineProperty() 类似。如果设置成功就会返回 true
Reflect.deleteProperty(target, propertyKey) 作为函数的delete操作符,相当于执行 delete target[name]
Reflect.get(target, propertyKey[, receiver]) 获取对象身上某个属性的值,类似于 target[name]
Reflect.getOwnPropertyDescriptor(target, propertyKey) 类似于 Object.getOwnPropertyDescriptor()如果对象中存在该属性,则返回对应的属性描述符,否则返回 undefined
Reflect.getPrototypeOf(target) 类似于 Object.getPrototypeOf()
Reflect.has(target, propertyKey) 判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同
Reflect.isExtensible(target) 类似于 Object.isExtensible().
Reflect.ownKeys(target) 返回一个包含所有自身属性(不包含继承属性)的数组。(类似于 Object.keys(), 但不会受enumerable 影响).
Reflect.preventExtensions(target) 类似于 Object.preventExtensions()返回一个Boolean
Reflect.set(target, propertyKey, value[, receiver]) 将值分配给属性的函数。返回一个Boolean,如果更新成功,则返回true
Reflect.setPrototypeOf(target, prototype) 设置对象原型的函数。返回一个 Boolean,如果更新成功,则返回 true

悉心观察可以发现,这上方的所有属性,都来自Object,那么就有问题了,为什么Object上有的属性,还要单拿出来呢?

2.2 为什么要用ReflectObject不好吗?😟

这就又要提到js独有的抽象了🤪

要知道js可是一门花了整整10天才诞生的语言🤡,那也难免会有抽象的事情发生

点名批评undefined以及arguments😓,一个BYD不是关键字,一个传进去了还能改,属于是抽象巅峰了🤪

Object也不例外,有些方法会有局限性以及与标准实现不一致的地方

例如, Object.defineProperty() 用于定义一个新的属性,在成功时返回 true,但在失败时会抛出一个错误。成功失败返回类型不一致,而且还会报错,这很令人恼火😡

那咋办呢?

ECMA选择了和解决上面的argumentsbug一样的解决方法——再写一套不就完了???🤣

于是就有了咱们的Reflect

其实还有一个更重要的原因,也是我把ReflectProxy放一起讲的理由: Proxy 处理程序的方法与 Reflect 的方法一一对应,这意味着可以在 Proxy 的拦截器函数中直接使用 Reflect 的方法。这使得在创建复杂的代理行为时,能够轻松地维护默认行为,同时添加或修改某些特定的行为。

3. 总结🤩

proxyreflect都是很强大的工具,两者结合可以轻松地维护默认行为,同时添加或修改某些特定的行为,实现更高自由度的编程,真的很酷好吗😏

如果想继续深入了解 Reflect以及Proxy, 可以去MDN上逛逛:

Reflect

Proxy


Reflect & Proxy
http://baidu.com/2023/12/17/Reflect/
作者
KB
发布于
2023年12月17日
许可协议