JS红宝书-第九章-代理与反射

本章总结了代理与反射的内容

代理

代理为开发者提供了拦截并向基本操作嵌入额外行为的能力

基础声明

有如下几个注意要点

  • 对proxy的修改,假如没有进行拦截,原对象也会进行修改
  • 经proxy包装的对象与原对象进行比较,并非严格相等
  • 不能对Proxy对象使用instanceof操作服
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const testTarget = {
info:1
}

const handler = {}
const proxy = new Proxy(testTarget, handler)

proxy.info = 2
console.log(testTarget.info)
//2

console.log(testTarget === handler)
//false

//Proxy没有prototype,因此不能使用instanceof操作符
console.log(testTarget instanceof Proxy)
// TypeError: Function has non-object prototype

捕获器及API

get-trap参数

property, receiver ``` 分别代表访问对象,访问属性,proxy参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

```javascript
const testTarget = {
info:1
}

const handler = {
get(target, property, receiver) {
console.log(target, property, receiver)
return 1
}
}
const proxy = new Proxy(testTarget, handler)
console.log(proxy.aaa)
// 1

trap invariant

如果对象存在不可写且不可配置的属性,会抛出异常TypeError

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const testTarget = {
}

Object.defineProperty(
testTarget,
"info",
{
writable: false,
configurable: false,
value:"hi"
}
)

let handler = {
get(target, property, receiver) {
return 123
}
}

const proxy = new Proxy(testTarget, handler)
console.log(proxy.info)
//uncaught TypeError: 'get' on proxy: property 'info' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value (expected 'hi' but got '123')

可撤销代理

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
const testTarget = {
}

Object.defineProperty(
testTarget,
"info",
{
writable: false,
configurable: false,
value:"hi"
}
)

let handler = {
get(target, property, receiver) {
return 123
}
}

const {proxy, revoke} = Proxy.revocable(testTarget, handler)
console.log(proxy.info)
//uncaught TypeError: 'get' on proxy: property 'info' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value (expected 'hi' but got '123')
revoke()
console.log(proxy.info)
//uncaught TypeError: Cannot perform 'get' on a proxy that has been revoked

可能出现的问题

  • this函数指向不同
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const testTarget = {
testFunc() {
console.log(this === testTarget)
}
}

let handler = {
get(target, property, receiver) {
console.log(this===target)
return Reflect.get(...arguments)
}
}

const proxy = new Proxy(testTarget,handler)
proxy.testFunc()
//false
testTarget.testFunc()
//true
  • 因此造成与内部API的不匹配
1
2
3
4
const target = new Date();
const proxy = new Proxy(target, {});
console.log(proxy instanceof Date); // true
proxy.getDate(); // TypeError: 'this' is not a Date object

to fix:

1
2
3
4
5
const target = new Date();
const Pro = new Proxy(Date, {});
const proxy = new Pro()
console.log(proxy instanceof Date); // true
proxy.getDate(); // 2

代理捕获器

get

参数:targetpropertyreceiver
拦截操作:

  • proxy[property]
  • proxy.property
  • Reflect.get(target, property, receiver)

set

参数:target, property, value, receiver
拦截:

  • proxy[property] = val
  • proxy.property = val
  • Object.create(proxy)[property] = value
  • Reflect.set(obj,property,val)

代理的用途

  • 跟踪属性访问
  • 隐藏属性
  • 属性验证
  • 构造器参数验证

一些面试题

1. Reflect对象和Object对象的区别?

1
2
3
Proxy定义了拦截的行为,
Reflect集合了部分Object的操作,未来的操作将部署在Reflect
Proxy的函数负责的是:拦截并定义拦截时具体的操作;Reflect的静态函数负责的是:最终执行对象的操作,因此保持一致

2. vue3如何实现双向绑定?与vue2有何区别?

关于绑定的实现

1
2
3
vue3通过proxy实现双向绑定,可以监听到内部数据变化
vue2不可以,需要使用$set更新视图
vue2使用Object.defineProperty监听变化

下面是简单的一个数据绑定的实现

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
<html>
<title>
testTitle
</title>
<body>
<h1 id="tree-val1">123</h1>
</body>
<script>
let handler = {
set(obj, property, val) {
console.log(obj,property)
Reflect.set(...arguments)
const ele = document.getElementById("tree-"+property)
if(ele!==null&&ele!==undefined) {
ele.innerHTML = val
}
}
}
let obj = {
val1 : "123"
}
let proxy = new Proxy(obj, handler)

setTimeout(()=>{
proxy.val1 = "456"
console.log(proxy.val1)
},2000)
</script>
</html>
1
Object.defineProperties()

vue2实现
vue3实现

3. getter和setter与proxy的区别

1
2
3
4
5
6
1. Proxy 是对整个对象的代理,而 Object.defineProperty 只能代理某个属性。
2. 对象上新增属性,Proxy 可以监听到,Object.defineProperty 不能。
3. 数组新增修改,Proxy 可以监听到,Object.defineProperty 不能。
4. 若对象内部属性要全部递归代理,Proxy 可以只在调用的时候递归,而 Object.definePropery 需要一次完成所有递归,性能比 Proxy 差。
5. Proxy 不兼容 IE,Object.defineProperty 不兼容 IE8 及以下
6. Proxy 使用上比 Object.defineProperty 方便多。

JS红宝书-第九章-代理与反射
https://tech.jasonczc.cn/2022/language/javascript/redbook/9-proxy-and-reflect/
作者
CZY
发布于
2022年4月29日
许可协议