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选择了和解决上面的arguments
bug一样的解决方法——再写一套不就完了???🤣
于是就有了咱们的Reflect
其实还有一个更重要的原因,也是我把Reflect
和Proxy
放一起讲的理由: Proxy
处理程序的方法与 Reflect
的方法一一对应,这意味着可以在 Proxy
的拦截器函数中直接使用 Reflect
的方法。这使得在创建复杂的代理行为时,能够轻松地维护默认行为,同时添加或修改某些特定的行为。
3. 总结🤩
proxy
和reflect
都是很强大的工具,两者结合可以轻松地维护默认行为,同时添加或修改某些特定的行为,实现更高自由度的编程,真的很酷好吗😏
如果想继续深入了解 Reflect
以及Proxy
, 可以去MDN上逛逛: