为什么 vue3 要选用 proxy,好处是什么?
提问
Object.defineProperty()和 proxy 的区别?
为什么 vue3 要选用 proxy,好处是什么?
proxy
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
Proxy 的用法,这个大家都知道
剖析一下内部实现 ECMAScript 2017 (ECMA-262)
可以看到接收两个参数(target,handler)
如果 target 是 undefined,报错
运行 ProxyCreate(
target
,handler
)
下面是 ProxyCreate 的实现
排除一下错误处理,核心代码从 5 开始先创建一个新的空对象 p,
设置 p 对象的内部方法(除了[call]]和[[Construct]])设置为[9.5 指定的定义,
然后设置 p 的 call 和 Construct 方法,
再设置内部属性[[ProxyTarget]]和[[ProxyHandler]]
返回对象 p
我们可以用它们拦截什么?
对于对象的大多数操作,JavaScript 规范中有一个所谓的“内部方法”,它描述了最底层的工作方式。例如 [[Get]]
,用于读取属性的内部方法,[[Set]]
,用于写入属性的内部方法,等等。这些方法仅在规范中使用,我们不能直接通过方法名调用它们。
Proxy 捕捉器会拦截这些方法的调用。它们在 proxy 规范 和下表中被列出。
对于每个内部方法,此表中都有一个捕捉器:可用于添加到 new Proxy
的 handler
参数中以拦截操作的方法名称:
对于对象的大多数操作,JavaScript 规范中有一个所谓的“内部方法”,它描述了最底层的工作方式。例如 [[Get]]
,用于读取属性的内部方法,[[Set]]
,用于写入属性的内部方法,等等。这些方法仅在规范中使用,我们不能直接通过方法名调用它们。
Proxy 捕捉器会拦截这些方法的调用。它们在 proxy 规范 和下表中被列出。
对于每个内部方法,此表中都有一个捕捉器:可用于添加到 new Proxy
的 handler
参数中以拦截操作的方法名称:
Reflect
Reflect
是一个内建对象,可简化 Proxy
的创建。
前面所讲过的内部方法,例如 [[Get]]
和 [[Set]]
等,都只是规范性的,不能直接调用。
Reflect
对象使调用这些内部方法成为了可能。它的方法是内部方法的最小包装。
尤其是,Reflect
允许我们将操作符(new
,delete
,……)作为函数(Reflect.construct
,Reflect.deleteProperty
,……)执行调用。这是一个有趣的功能,但是这里还有一点很重要。
对于每个可被 Proxy
捕获的内部方法,在 Reflect
中都有一个对应的方法,其名称和参数与 Proxy
捕捉器相同。
所以,我们可以使用 Reflect
来将操作转发给原始对象。
我们可以把捕捉器重写得更短:
Reflect
调用的命名与捕捉器的命名完全相同,并且接受相同的参数。它们是以这种方式专门设计的。
因此,return Reflect...
提供了一个安全的方式,可以轻松地转发操作,并确保我们不会忘记与此相关的任何内容。
proxy 的局限性
1. 无法代理内部插槽
许多内建对象,例如 Map
,Set
,Date
,Promise
等,都使用了所谓的“内部插槽”。
例如:
解决方法 在 get 的时候将 get 要返回的值先绑定目标对象后返回
2. 私有字段也和上面一样
3. peoxy != target
这个很好理解 ,代理对象和目标对象是不=== 的
总结
Proxy
是对象的包装器,将代理上的操作转发到对象,并可以选择捕获其中一些操作。
它可以包装任何类型的对象,包括类和函数。
语法为:
……然后,我们应该在所有地方使用 proxy
而不是 target
。代理没有自己的属性或方法。如果提供了捕捉器(trap),它将捕获操作,否则会将其转发给 target
对象。
我们可以捕获
get,set,deleteProperty 等操作
函数调用(apply 捕捉器)
new 操作(construct 捕捉器)
Reflect 旨在补充 Proxy。对于任意 Proxy
捕捉器,都有一个带有相同参数的 Reflect
调用。我们应该使用它们将调用转发给目标对象。
Proxy 的局限
无法代理内部对象的内部插槽
无法代理私有字段
代理对象和目标对象不相等
Object.defineProperty
Object.defineProperty()
方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
语法
描述
对象里目前存在的属性描述符有两种主要形式:数据(属性)描述符和存取描述符(访问器属性)。数据描述符是一个具有值的属性,该值可以是可写的,也可以是不可写的。存取描述符是由 getter 函数和 setter 函数所描述的属性。一个描述符只能是这两者其中之一;不能同时是两者。参考视频讲解:进入学习
属性描述符
value
— 值writable
— 如果为true
,则值可以被修改,否则它是只可读的。enumerable
— 如果为true
,则会被在循环中列出,否则不会被列出。configurable
— 如果为true
,则此属性可以被删除,这些特性也可以被修改,否则不可以。
访问器属性
get
—— 一个没有参数的函数,在读取属性时工作,set
—— 带有一个参数的函数,当属性被设置时调用,enumerable
—— 与数据属性的相同,configurable
—— 与数据属性的相同。
回答第一个问题
Object.defineProperty()和 proxy 的区别?
回答第二个问题
为什么 vue3 要选用 proxy,好处是什么?
能够代理任何对象包括数组和函数,对象
比 Object.defineProperty()更多的基本语义得操作(get,set,delete...)
不用循环遍历对象然后再使用 Object.defineProperty(),Proxy 可以代理对象内所有的属性。
Object.defineProperty()只能劫持对象的属性(给对象添加属性 vue 无法检测到)
评论