Skip to content

代理模式 ​

代理模式是我们不直接与目标变量进行交互, 而是与目标变量的代理进行交互. 这里的交互方向有点类似单例模式, 但是代理模式相比于单例模式可以增加许多自定义功能, 比如一些属性的校验, 额外信息的返回等. 这里我们讲的代理模式主要是基于Proxy 实现(如Vue3的响应式).

1. 基本的代理模式 ​

在这里我们先展示最基本的代理模式

javascript
const foo = {
  name: 'foo',
  get value() {
    return this.name
  }
}

const proxy = new Proxy(foo, {
  get(target, p, receiver) {
    console.log(`proxy value is ----- ${target[p]}`)
    return target[p]
  }
})

// proxy value is ----- foo
proxy.value

const bar = {
  name: 'bar'
}
Object.setPrototypeOf(bar, proxy)

// proxy value is ----- foo
bar.value

在上面代码我们可以看到, 设定bar 对象的proxy为上面定义的, 进而bar 继承foo 函数里面的value 属性. 但是这样设定后, bar.value 的值却指向了foo 的name 属性.

我们可以关注到proxy 的get 方法有一个receiver 参数, 而这也就是下面更严谨的写法的关键点.

2. 严谨的代理模式 ​

参考部分文章: https://www.zhihu.com/question/460133198

javascript
const foo = {
  name: 'foo',
  get value() {
    return this.name
  }
}

const proxy = new Proxy(foo, {
  get(target, p, receiver) {
    console.log(`proxy value is ----- ${Reflect(target, p, receiver)}`)
    return Reflect(target, p, receiver)
  }
})

// proxy value is ----- foo
proxy.value

const bar = {
  name: 'bar'
}
Object.setPrototypeOf(bar, proxy)

// proxy value is ----- bar
bar.value

可以看到, 在上面的get 方法中我们使用了Reflect 反射进行数据的获取. 虽然上面代码中只劫持了数据的get 方法, 但是开发中可能有许多其他的方法, 比如ownKeys, has, Reflect 中已经帮我们定义好了这些方法, 我们不用再自己写, 所以更推荐统一用Reflect 方法进行数据的获取.

除此之外, 我们观察到Reflect 后面传入了第三个参数receiver , 正是这个参数使得我们获取到了bar 对象自己的value 值. 加上这个参数, Reflect 方法才知道this 的指向, 找到正确的属性值.

在MDN上对receiver 是这么描述的(原文这里)

如果target对象中指定了getter,receiver则为getter调用时的this值。

由此我们可以知道, bar 对象继承了foo 对象的方法, 在获取value 值的时候this 指向的是调用的地方, 即bar 本身, 进而得到正确的值.