Proxy

参考

Object.definePorperty() 与 proxy 都可以监听对象的变化

但 Object.definePorperty() 进行数据监听是比较麻烦的,需要大量的手动处理。对象新增加一个属性时,也需要手动去监听这个新增属性。对于数组得监听还有一系列问题。

所以都什么时代了,还用传统数据监听方式啊,来试试**proxy**

Proxy

1. 基本使用

语法:const p = new Proxy(target, handler) 参数:

TS 定义

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
interface ProxyConstructor {
new <T extends object>(target: T, handler: ProxyHandler<T>): T;
revocable<T extends object>(
target: T,
handler: ProxyHandler<T>
): { proxy: T; revoke: () => void };
}

interface ProxyHandler<T extends object> {
get?(target: T, p: string | symbol, receiver: any): any;
set?(target: T, p: string | symbol, value: any, receiver: any): boolean;
apply?(target: T, thisArg: any, argArray: any[]): any;
construct?(target: T, argArray: any[], newTarget: Function): object;
defineProperty?(
target: T,
p: string | symbol,
attributes: PropertyDescriptor
): boolean;
deleteProperty?(target: T, p: string | symbol): boolean;
getOwnPropertyDescriptor?(
target: T,
p: string | symbol
): PropertyDescriptor | undefined;
getPrototypeOf?(target: T): object | null;
has?(target: T, p: string | symbol): boolean;
isExtensible?(target: T): boolean;
ownKeys?(target: T): ArrayLike<string | symbol>;
preventExtensions?(target: T): boolean;
setPrototypeOf?(target: T, v: object | null): boolean;
}
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
//定义一个需要代理的对象
let person = {
age: 0,
school: "西电",
};

//定义handler对象
let hander = {
get(obj, key) {
// 如果对象里有这个属性,就返回属性值,如果没有,就返回默认值66
return key in obj ? obj[key] : 66;
},
set(obj, key, val) {
obj[key] = val;
return true;
},
};

//把handler对象传入Proxy
let proxyObj = new Proxy(person, hander);

// 测试get能否拦截成功
console.log(proxyObj.age); //输出0
console.log(proxyObj.school); //输出西电
console.log(proxyObj.name); //输出默认值66

// 测试set能否拦截成功
proxyObj.age = 18;
console.log(proxyObj.age); //输出18 修改成功

但是一旦使用 Proxy,如果想要读写操作生效,我们就要对 Proxy 的实例对象**proxyObj**进行操作。**proxyObj** set 后,原对象 obj 也会被 set,但原对象的 get 无法触发**handler**

另外,MDN 上明确指出 set()方法应该返回一个布尔值,否则会报错TypeError

2. 轻松解决 object.definePorperty() 的问题

Object.defineProperty 的问题

  • 一次只能对一个属性进行监听,需要遍历来对所有属性监听。这个我们在上面已经解决了。
  • 在遇到一个对象的属性还是一个对象的情况下,需要递归监听。
  • 对于对象的新增属性,需要手动监听
  • 对于数组通过 push、unshift 方法增加的元素,也无法监听

用了 Proxy 后可以完美解决以上问题

3. proxy 的 13 种拦截

  • get(target, propKey, receiver):拦截对象属性的读取,比如 proxy.foo 和 proxy[‘foo’]。
  • set(target, propKey, value, receiver):拦截对象属性的设置,比如 proxy.foo = v 或 proxy[‘foo’] = v,返回一个布尔值。
  • has(target, propKey):拦截 propKey in proxy 的操作,返回一个布尔值。
  • deleteProperty(target, propKey):拦截 delete proxy[propKey]的操作,返回一个布尔值。
  • ownKeys(target):拦截 Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for…in 循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而 Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
  • getOwnPropertyDescriptor(target, propKey):拦截 Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
  • defineProperty(target, propKey, propDesc):拦截 Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
  • preventExtensions(target):拦截 Object.preventExtensions(proxy),返回一个布尔值。
  • getPrototypeOf(target):拦截 Object.getPrototypeOf(proxy),返回一个对象。
  • isExtensible(target):拦截 Object.isExtensible(proxy),返回一个布尔值。
  • setPrototypeOf(target, proto):拦截 Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
  • apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如 proxy(…args)、proxy.call(object, …args)、proxy.apply(…)。
  • construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如 new proxy(…args)。

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!