Skip to content

代理模式

什么是代理模式

比如每个明星都会有一个经纪人,如果想请这个明星参与演出,那么都会先联系到经纪人,经纪人将各个方面都谈好之后,再吧合同交给明星来签

简单的代理

下面举个例子来实现一个简单的代理

比如下面一个场景:小明想给小红送上一束花,那么不使用代理模式的情况下,代码是这样的:

js
// 花
class Flower {}

// 小明
const xMing = {
  // 送花
  sendFlower(target) {
    const flower = new Flower()
    target.receiveFlower(flower)
  }
}

// 小红
const xHong = {
  // 收到花
  receiveFlower(flower) {
    console.log('收到花', flower)
  }
}

xMing.sendFlower(xHong) // 收到花 Flower {}

那么使用代理的方式呢,比如小明不想直接给小红送花,所以找到了小刚,让小刚代替自己去送花,那么代码如下:

js
// 花
class Flower {}

// 小明
const xMing = {
  // 送花
  sendFlower(target) {
    const flower = new Flower()
    target.receiveFlower(flower)
  }
}

// 小刚
const xGang = {
  // 收到花
  receiveFlower(flower) {
    // 再送给小红
    xHong.receiveFlower(flower)
  }
}

// 小红
const xHong = {
  // 收到花
  receiveFlower(flower) {
    console.log('收到花', flower)
  }
}

xMing.sendFlower(xGang) // 收到花 Flower {}

这样,上面就完成了一个简单的代理模式

上面代码中,看似小明直接给小红送花,和通过代理对象小刚再送花,二者看起来没用任何区别,引入一个代理对象,只不过是把事情搞复杂了而已。

的确,对于这样的代理,是毫无意义的

但是现在要切换一下背景的设定:如果在小红心情好的时候,才会收取送来的花,而在心情不好的时候,是不会收花的,而小明不能知道小红什么时候心情好,只要小刚知道,只要小刚知道小红心情好的时候,才会把花送给小红,那么代码如下:

js
// 花
class Flower {}

// 小明
const xMing = {
  // 送花
  sendFlower(target) {
    const flower = new Flower()
    target.receiveFlower(flower)
  }
}

// 小刚
const xGang = {
  // 收到花
  receiveFlower(flower) {
    // 监听小红心情
    xHong.listenGoodMood(() => {
      xHong.receiveFlower(flower)
    })
  }
}

// 小红
const xHong = {
  listenGoodMood(callback) {
    // 2 秒后会心情好
    setTimeout(() => {
      callback()
    }, 2000)
  },
  // 收到花
  receiveFlower(flower) {
    console.log('收到花', flower)
  }
}

xMing.sendFlower(xGang) // 收到花 Flower {}

虚拟代理

还是上面例子,比如 new Flower() 是一个开销很大的操作,那么可以直接让小刚来执行

js
// 花
class Flower {}

// 小刚
const xGang = {
  // 收到花
  receiveFlower() {
    // 监听小红心情
    xHong.listenGoodMood(() => {
      const flower = new Flower()
      xHong.receiveFlower(flower)
    })
  }
}

// 小红
const xHong = {
  listenGoodMood(callback) {
    // 2 秒后会心情好
    setTimeout(() => {
      callback()
    }, 2000)
  },
  // 收到花
  receiveFlower(flower) {
    console.log('收到花', flower)
  }
}

xGang.receiveFlower() // 收到花 Flower {}

虚拟代理实现图片预加载

有时候由于图片过大和网络不佳,会导致图片加载很慢,通常是会展示一个空白的状态,或者是展示一个 loading 图片,等加载好之后再添加到 img 中去

下面实现一个使用代理模式实现的图片预加载

js
const myImage = (function () {
  const img = document.createElement('img')
  document.body.appendChild(img)
  return function (src) {
    img.src = src
  }
})()

const proxyImg = (function () {
  const img = new Image()

  img.onload = function () {
    myImage(this.src)
  }

  return function (src) {
    myImage('./src/1.jpg')
    img.src = src
  }
})()

proxyImg(
  'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg'
)

缓存代理

接下来通过一个阶乘来实现缓存代理

下面是不使用代理的方式:

js
function add() {
  let res = 1
  for (let i = 0; i < arguments.length; i++) {
    res *= arguments[i]
  }
  return res
}

console.log(add(1, 2, 3)) // 6

接下来加入代理函数:

js
function add() {
  let res = 1
  for (let i = 0; i < arguments.length; i++) {
    res *= arguments[i]
  }
  return res
}

const proxyAdd = (function () {
  const cache = {}
  return function () {
    const args = Array.prototype.join.call(arguments, ',')
    // 如果缓存对象中存在则不需要运算直接返回
    if (args in cache) {
      return cache[args]
    }
    return (cache[args] = add.apply(this, arguments))
  }
})()

console.log(proxyAdd(1, 2, 3)) // 6
console.log(proxyAdd(1, 2, 3)) // 6

写在最后

虽然代理模式非常有用,但我们在编写程序的时候,往往不需要先去预测是否需要使用代理模式。当真正发现不方便直接访问某个对象的时候,再编写代理也不迟