# Object的变化侦测

经过上一小节,相信大家对于Proxy有一定的了解,本节将介绍Proxy如何实现Object的变化侦测。

# 监听get和set

我们使用Proxy定义一个简单的方法

const obj = {
  a: 1
}

const handlers = {
  get(target, key) {
    console.log(`getting ${key}`)
    return target[key]
  },
  set(target, key, value) {
    console.log(`setting ${key} to ${value}`)
    return Reflect.set(target, key, value);
  }
}

const state = new Proxy(obj, handlers);

state.a; // getting a
state.a = 2; // setting a to 2
成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

执行上述代码,你会发现Proxy会打印出gettingsetting,说明Proxy已经实现了最简单的对Objectgetset的变化侦测。

有些同学可能会有疑问,为什么set方法需要返回Reflect.set(target, key, value);,而不是直接返回target[key] = value

因为Proxyset方法需要返回一个布尔值,代表set方法是否成功执行,如果直接返回target[key] = value,那么Proxy就无法知道set方法是否成功执行了。

当然我们有很多方法去判断是否成功执行,但本着ProxyReflect对应的原则,这里我们使用ES6提供的对应的方法Reflect.set(target, key, value)

同样的,我们也可以把get中的实现改为Reflect.get(target, key)

# 依赖收集和派发更新

我们之前说过,变化侦测,是指监测数据的变化情况,并在发生变化时更新视图。 要实现这个流程,我们除了监听数据之外,还需要收集对应的依赖和当依赖发生变更时,派发更新。

由此,我们可以将上述代码,改成如下

const obj = {
  a: 1
}

// 依赖收集
function track(target, key) {
  console.log('依赖收集', key);
}
// 派发更新
function trigger(target, key) {
  console.log('触发更新', key);
}

const handlers = {
  get(target, key) {
    console.log(`getting ${key}`)
    // 依赖收集
    track(target, key);
    return Reflect.get(target, key);
  },
  set(target, key, value) {
    console.log(`setting ${key} to ${value}`)
    // 派发更新
    trigger(target, key);
    return Reflect.set(target, key, value);
  }
}

const state = new Proxy(obj, handlers);

state.a;
state.a = 2;
成功
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
31
32

执行上述代码,可以得到如下打印内容

// 依赖收集
// getting a
// setting a to 2
// 触发更新
// getting a
成功
1
2
3
4
5

那到目前为止,我们已经利用Proxy实现了一个简单的响应式系统,能够监听数据的变化,收集依赖和派发对应的更新。我们接下来继续完善这个响应式系统的细节。