Proxy
2023-08-10 14:47:57
proxy 是 ES6+ 的语法,用于对数据劫持,从而实现基本操作的拦截和自定义,如属性查找、赋值、枚举、函数调用等,Vue3 的响应式原理就是使用 proxy 实现的
基本使用
先来创建一个 Proxy 对象,要求传入两个参数,第一个参是被代理的对象,第二个参是捕获器
ts
const target = { a: 1 }
const handler = {}
const p = new Proxy(target, handler)
console.log(p) // { a: 1 }
1
2
3
4
5
2
3
4
5
当我们通过 proxy 对象来改变对象属性时,target 对象也会跟着改变
ts
p.a = 2
console.log(p) // { a: 2 }
console.log(target) // { a: 2 }
1
2
3
2
3
同样的改变 target 对象属性时 proxy 对象也会同步改变
ts
target.a = 3
console.log(p) // { a: 3 }
console.log(target) // { a: 3 }
1
2
3
2
3
捕获器
在 handler 中定义一个捕获器,会在 get 被触发时调用
ts
const target = { a: 1 }
const handler = {
get: () => {
return 'get is captured'
}
}
const p = new Proxy(target, handler)
console.log(p.a) // get is captured
console.log(target.a) // 1
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
捕获器可以访问到对应的参数,get() 捕获器可以接收到目标对象、要查询的属性和代理对象三个参数
ts
const target = { a: 1 }
const handler = {
get: (trapTarget, property, receiver) => {
console.log('trapTarget:', trapTarget)
console.log('property:', property)
console.log('receiver:', receiver)
}
}
const p = new Proxy(target, handler)
console.log(p.a)
// 打印结果
// trapTarget: { a: 1 }
// property: a
// receiver: { a: 1 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
有了这些参数就可以自定义一些捕获方法
撤销
Proxy 暴露了 revocable()方法,这个方法支持撤销代理对象与目标对象的关联
撤销代理的操作是不可逆的。而且,撤销函数(revoke())是幂等的,调用多少次的结果都一样。撤销代理之后 再调用代理会抛出 TypeError
ts
const target = {
a: 1
}
const handler = {
get() {
return 2
}
}
const { proxy, revoke } = Proxy.revocable(target, handler)
console.log(proxy.a) // 2
console.log(target.a) // 1
revoke()
console.log(proxy.a) // TypeError
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
代理模式
跟踪属性访问
ts
const person = {
name: 'Jake'
}
const proxy = new Proxy(person, {
get(target, property, receiver) {
console.log(`Getting ${property}`)
return Reflect.get(...arguments)
},
set(target, property, value, receiver) {
console.log(`Setting ${property}=${value}`)
return Reflect.set(...arguments)
}
})
proxy.name // Getting name
proxy.age = 27 // Setting age=27
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
属性验证
因为所有赋值操作都会触发 set()捕获器,所以可以根据所赋的值决定是允许还是拒绝赋值:
ts
const target = {
onlyNumbersGoHere: 0
}
const proxy = new Proxy(target, {
set(target, property, value) {
if (typeof value !== 'number') {
return false
} else {
return Reflect.set(...arguments)
}
}
})
proxy.onlyNumbersGoHere = 1
console.log(proxy.onlyNumbersGoHere) // 1
proxy.onlyNumbersGoHere = '2'
console.log(proxy.onlyNumbersGoHere) // 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16