发问
- Object.defineProperty()和 proxy 的区别?
- 为什么 vue3 要选用 proxy,益处是什么?
proxy
Proxy 对象用于创立一个对象的代理,从而实现基本操作的拦挡和自定义(如属性查找、赋值、枚举、函数调用等)。
Proxy 的用法,这个大家都晓得
const p = new Proxy(target, handler)
分析一下外部实现 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
参数中以拦挡操作的办法名称:
外部办法 | Handler 办法 | 何时触发 |
---|---|---|
[[Get]] |
get |
读取属性 |
[[Set]] |
set |
写入属性 |
[[HasProperty]] |
has |
in 操作符 |
[[Delete]] |
deleteProperty |
delete 操作符 |
[[Call]] |
apply |
函数调用 |
[[Construct]] |
construct |
new 操作符 |
[GetPrototypeOf]] |
getPrototypeOf |
[Object.getPrototypeOf |
[SetPrototypeOf]] |
setPrototypeOf |
[Object.setPrototypeOf |
[IsExtensible]] |
isExtensible |
[Object.isExtensible |
[PreventExtensions]] |
preventExtensions |
[Object.preventExtensions |
[DefineOwnProperty]] |
defineProperty |
[Object.defineProperty, Object.defineProperties |
[GetOwnProperty]] |
getOwnPropertyDescriptor |
[Object.getOwnPropertyDescriptor, for..in , Object.keys/values/entries |
[OwnPropertyKeys]] |
ownKeys |
[Object.getOwnPropertyNames, Object.getOwnPropertySymbols, for..in , Object.keys/values/entries |
Reflect
Reflect
是一个内建对象,可简化 Proxy
的创立。
后面所讲过的外部办法,例如 [[Get]]
和 [[Set]]
等,都只是规范性的,不能间接调用。
Reflect
对象使调用这些外部办法成为了可能。它的办法是外部办法的最小包装。
尤其是,Reflect
容许咱们将操作符(new
,delete
,……)作为函数(Reflect.construct
,Reflect.deleteProperty
,……)执行调用。这是一个乏味的性能,然而这里还有一点很重要。
对于每个可被 Proxy
捕捉的外部办法,在 Reflect
中都有一个对应的办法,其名称和参数与 Proxy
捕获器雷同。
所以,咱们能够应用 Reflect
来将操作转发给原始对象。
咱们能够把捕获器重写得更短:
get(target, prop, receiver) {return Reflect.get( ... arguments);
}
Reflect
调用的命名与捕获器的命名完全相同,并且承受雷同的参数。它们是以这种形式专门设计的。
因而,return Reflect...
提供了一个平安的形式,能够轻松地转发操作,并确保咱们不会遗记与此相关的任何内容。
proxy 的局限性
1. 无奈代理外部插槽
许多内建对象,例如 Map
,Set
,Date
,Promise
等,都应用了所谓的“外部插槽”。
例如:
let map = new Map();
let proxy = new Proxy(map, {});
proxy.set('test', 1); // Error
解决办法 在 get 的时候将 get 要返回的值先绑定指标对象后返回
let map = new Map();
let proxy = new Proxy(map, {get(target, prop, receiver) {let value = Reflect.get(...arguments);
return typeof value == 'function' ? value.bind(target) : value;
}
});
proxy.set('test', 1);
alert(proxy.get('test')); // 1(工作了!)
2. 公有字段也和下面一样
3. peoxy != target
这个很好了解,代理对象和指标对象是不 === 的
总结
Proxy
是对象的包装器,将代理上的操作转发到对象,并能够抉择捕捉其中一些操作。
它能够包装任何类型的对象,包含类和函数。
语法为:
let proxy = new Proxy(target, {/* trap */});
……而后,咱们应该在所有中央应用 proxy
而不是 target
。代理没有本人的属性或办法。如果提供了捕获器(trap),它将捕捉操作,否则会将其转发给 target
对象。
咱们能够捕捉
- get,set,deleteProperty 等操作
- 函数调用(apply 捕获器)
- new 操作(construct 捕获器)
Reflect 旨在补充 Proxy。对于任意 Proxy
捕获器,都有一个带有雷同参数的 Reflect
调用。咱们应该应用它们将调用转发给指标对象。
Proxy 的局限
- 无奈代理外部对象的外部插槽
- 无奈代理公有字段
- 代理对象和指标对象不相等
Object.defineProperty
Object.defineProperty()
办法会间接在一个对象上定义一个新属性,或者批改一个对象的现有属性,并返回此对象。
语法
Object.defineProperties(obj, props)
形容
对象里目前存在的属性描述符有两种次要模式:数据(属性)描述符 和存取描述符 (拜访器属性)。 数据描述符 是一个具备值的属性,该值能够是可写的,也能够是不可写的。存取描述符 是由 getter 函数和 setter 函数所形容的属性。一个描述符只能是这两者其中之一;不能同时是两者。参考视频解说:进入学习
属性描述符
value
— 值writable
— 如果为true
,则值能够被批改,否则它是只可读的。enumerable
— 如果为true
,则会被在循环中列出,否则不会被列出。configurable
— 如果为true
,则此属性能够被删除,这些个性也能够被批改,否则不能够。
拜访器属性
get
—— 一个没有参数的函数,在读取属性时工作,set
—— 带有一个参数的函数,当属性被设置时调用,enumerable
—— 与数据属性的雷同,configurable
—— 与数据属性的雷同。
答复第一个问题
Object.defineProperty()和 proxy 的区别?
Object.defineProperty | Proxy |
---|---|
新增 / 批改一个对象的属性,定义其形容,返回该对象 | 代理指标对象,对其操作拦挡,返回代理对象 |
有数据描述符和拜访器描述符两种 | 对其 13 种操作进行拦挡 |
只能代理惯例对象 | 能够代理任何对象(函数,数组,类) |
– | 不能代理外部对象的外部插槽 |
答复第二个问题
为什么 vue3 要选用 proxy,益处是什么?
- 可能代理任何对象包含数组和函数,对象
- 比 Object.defineProperty()更多的根本语义得操作(get,set,delete…)
- 不必循环遍历对象而后再应用 Object.defineProperty(),Proxy 能够代理对象内所有的属性。
- Object.defineProperty()只能劫持对象的属性(给对象增加属性 vue 无奈检测到)