Reflect & Proxy
Reflect和Proxy 🥳
1. Proxy 🕶️
1.1 语法🤡
我们可以使用 Proxy() 构造器来创建一个新的 Proxy 对象。构造器接收两个主要参数:
target被代理的对象handler被代理对象上的自定义行为
像这样:
1 | |
一个空的 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.getOwnPropertyNames和Object.getOwnPropertySymbols调用劫持。 |
| handler.preventExtensions() | Object.preventExtensions调用劫持 |
| handler.set() | 设置属性值劫持 |
| handler.setPrototypeOf() | Object.setPrototypeOf调用劫持 |
因为是对对象功能的劫持,所以功能差不多与
Object的方法名一一对应🫨
1.3 示例💩
1 | |
🤪猜猜会输出什么嘞
🤪打瓦的闭嘴
Proxy第一个参数都是target,指定代理的对象;第二个参数
prop指定代理属性名;第三个参数
receiver用于指定this指向,如果不加上可能会导致this不会正确配置(不太懂,没找到例子🤔)
1.4 有getter和setter了,为什么还要有个proxy嘞🦢
虽然 Getter 和 Setter 提供了对单个属性操作的控制,但它们不能直接应用于整个对象或拦截其他类型的操作。Proxy 提供了对整个对象操作的完全控制,可以拦截和自定义几乎所有对对象的操作。这使得 Proxy 在进行复杂操作或创建高级抽象时非常有用。
还是那段代码,想想看要是想动态加上一个属性m4,不管输入什么最后都打印出0。用getter或者setter怎么写?显然是不可能的,因为getter和setter管不到defineProperties, 但是要换成proxy`就很简单了
1 | |
但是
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 为什么要用Reflect,Object不好吗?😟
这就又要提到js独有的抽象了🤪
要知道js可是一门花了整整10天才诞生的语言🤡,那也难免会有抽象的事情发生
点名批评
undefined以及arguments😓,一个BYD不是关键字,一个传进去了还能改,属于是抽象巅峰了🤪
Object也不例外,有些方法会有局限性以及与标准实现不一致的地方
例如,
Object.defineProperty()用于定义一个新的属性,在成功时返回true,但在失败时会抛出一个错误。成功失败返回类型不一致,而且还会报错,这很令人恼火😡
那咋办呢?
ECMA选择了和解决上面的argumentsbug一样的解决方法——再写一套不就完了???🤣
于是就有了咱们的Reflect
其实还有一个更重要的原因,也是我把Reflect和Proxy放一起讲的理由: Proxy 处理程序的方法与 Reflect 的方法一一对应,这意味着可以在 Proxy 的拦截器函数中直接使用 Reflect 的方法。这使得在创建复杂的代理行为时,能够轻松地维护默认行为,同时添加或修改某些特定的行为。
3. 总结🤩
proxy和reflect都是很强大的工具,两者结合可以轻松地维护默认行为,同时添加或修改某些特定的行为,实现更高自由度的编程,真的很酷好吗😏
如果想继续深入了解 Reflect以及Proxy, 可以去MDN上逛逛: