設計模式-原來這就是代理模式(三)

這是我參與更文挑戰的第1天,活動詳情查看: 更文挑戰javascript

1、什麼是代理模式

代理模式是爲一個對象提供一個代用品或佔位符,以便控制對它的訪問。html

代理模式的關鍵是,當客戶不方便直接訪問一個對象或者不知足須要的時候,提供一個替身對象來控制對這個對象的訪問,客戶實際上訪問的是替身對象。java

2、模擬場景

1. 小明送花給小白

1.1 傳統作法

傳統作法是小明直接把花送給小白,小白接收到花,代碼以下:git

const Flower = function () {
	return '玫瑰🌹'
}

const xiaoming = {
	sendFlower: target => {
		const flower = new Flower()
		target.receiveFlower(flower)
	}
}

const xiaobai = {
	receiveFlower: flower => {
		console.log('收到花', flower)
	}
}

xiaoming.sendFlower(xiaobai)
複製代碼

1.2 代理模式

可是,小明並不認識小白,他想要經過小代,幫他打探小白的狀況,在小白心情好的時候送花,這樣成功率更高。代碼以下:github

const Flower = function () {
	return '玫瑰🌹'
}

const xiaoming = {
	sendFlower: target => {
		const flower = new Flower()
		target.receiveFlower(flower)
	}
}

const xiaodai = {
	receiveFlower: flower => {
		xiaobai.listenGoodMood().then(() => {
			xiaobai.receiveFlower(flower)
		})
	}
}

const xiaobai = {
	receiveFlower: flower => {
		console.log('收到花', flower)
	},
	listenGoodMood: fn => {
		return new Promise((reslove, reject) => {
			// 10秒後,心情變好
			reslove()
		})
	}
}

xiaoming.sendFlower(xiaodai)
複製代碼

以上,小明經過小代,監聽到小白心情的心情變化,選擇在小白心情好時送花給小白。不只如此,小代還能夠作如下事情:web

  1. 幫助小白過濾掉一些送花的請求,這就叫作保護代理;
  2. 幫助小明,在小白心情好時,再執行買花操做,這就叫作虛擬代理。虛擬代理把一些開銷很大的對象,延遲到真正須要它的時候纔去建立。

3、實際場景

1. 圖片預加載

圖片預加載時一種常見的技術,若是直接給img標籤節點設置src屬性,因爲圖片過大或網絡不佳,圖片的位置每每有一段時間時空白。ajax

1.1 傳統作法

const myImage = (() => {
	const imgNode = document.createElement('img')
	document.body.appendChild(imgNode)

	return {
		setSrc: src => {
			imgNode.src = src
		}
	}
})()

myImage.setSrc('https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fa98e67c4708449eb6894c7133d93774~tplv-k3u1fbpfcp-watermark.image')

複製代碼

經過開發者工具把網速設置爲 5kb/s時,會發如今很長一段時間內,圖片位置是空白的。設計模式

image.png

1.2 虛擬代理

下面用虛擬代理優化該功能,把加載圖片的操做交給代理函數完成,在圖片加載時,先用一張loading圖佔位,當圖片加載成功後,再把它填充進img節點。緩存

代碼以下:服務器

const myImage = (() => {
	const imgNode = document.createElement('img')
	document.body.appendChild(imgNode)

	return {
		setSrc: src => {
			imgNode.src = src
		}
	}
})()

const loadingSrc = '../../../../img/loading.gif'
const imgSrc = 'https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fa98e67c4708449eb6894c7133d93774~tplv-k3u1fbpfcp-watermark.image'

const proxyImage = (function () {
	const img = new Image()
	img.onload = () => {
		myImage.setSrc(img.src)
	}

	return {
		setSrc: src => {
			myImage.setSrc(loadingSrc)
			img.src = src
		}
	}
})()

proxyImage.setSrc(imgSrc)
複製代碼

上述代碼有如下優勢:

  1. 經過 proxyImage 控制了對 MyImage 的訪問,在 MyImage 未加載成功以前,使用 loading 圖佔位;

  2. 踐行單一職責原則,給 img 節點設置 src 的函數 MyImage,預加載圖片的函數 proxyImage,都只有一個職責;

  3. 踐行開放-封閉原則,給 img 節點設置 src 和預加載圖片的功能,被隔離在兩個對象裏,它們能夠各自變化不影響對方。

2.合併HTTP請求

假設咱們要實現一個同步文件的功能,經過複選框,當複選框選中的時候,將該複選框對應的id傳給服務器,告訴服務器須要同步 id 對應的文件。

思考一下,會發現,若是每選中一個複選框,就請求一次接口,假設 1s 內選中了 10 個複選框,那麼就要發送 10 次請求。

2.1 虛擬代理

能夠經過虛擬代理來優化上述作法,新增一個代理,幫助複選框發起同步文件的請求,收集在這 1s 內的請求,1s 後再一塊兒把這些文件 id 發送到服務器。

代碼以下:

<!DOCTYPE html>
<html>
<meta charset="utf-8" />
<head>
	<title></title>
</head>
<body>
  a <input type="checkbox" value="a" />
  b <input type="checkbox" value="b" />
  c <input type="checkbox" value="c" />
  d <input type="checkbox" value="d" />
	<script type="text/javascript" src="index.js">
	</script>
</body> 
</html>
複製代碼
const synchronousFile = cache => {
  console.log('開始同步文件,id爲:'+ cache.join('/'))
}

const proxySynchronousFile = (() => {
  const cache = []

  let timer

  return id => {
    console.log(id)
    cache.push(id)

    if (timer) {
      return
    }

    timer = setTimeout(() => {
      synchronousFile(cache)
      clearTimeout(timer)
      timer = null
      cache.length = 0
    }, 2000)
  }
})()

const checkbox = document.getElementsByTagName('input')

Array.from(checkbox).forEach(i => {
  console.log(i)
  i.onclick = () => {
    if (i.checked) {
      proxySynchronousFile(i.value)
    }
  }
})


複製代碼

github 源碼地址

3. ajax異步請求數據

在列表須要分頁時,同一頁的數據理論上只須要去後臺拉取一次,能夠把這些拉取過的數據緩存下來,下次請求時直接使用緩存數據

3.1 緩存代理

使用緩存代理實現上述功能,代碼以下:

(async function () {
  function getArticle (currentPage, pageSize) {
    console.log('getArticle', currentPage, pageSize)
    // 模擬一個ajax請求
    return new Promise((resolve, reject) => {
      resolve({
        ok: true,
        data: {
          list: [],
          total: 10,
          params: {
            currentPage, 
            pageSize
          }
        }
      })
    })
  }
  
  const proxyGetArticle = (() => {
    const caches = []
  
    return async (currentPage, pageSize) => {
  
      const cache = Array.prototype.join.call([currentPage, pageSize],',')
  
      if (cache in caches) {
        return caches[cache]
      }
      const { data, ok } = await getArticle(currentPage, pageSize)
  
      if (ok) {
        caches[cache] = data
      }
  
      return caches[cache]
    }
  })()

  // 搜索第一頁
  await proxyGetArticle(1, 10)
  
  // 搜索第二頁
  await proxyGetArticle(2, 10)

  // 再次搜索第一頁
  await proxyGetArticle(1, 10)
  
})()


複製代碼

經過緩存代理,在第二次請求第一頁的數據時,直接在緩存數據中拉取,無須再次從服務器請求數據。

4、小結

上面根據實際場景介紹了虛擬代理和緩存代理的作法。

當咱們不方便直接訪問某個對象時,找一個代理方法幫咱們去訪問該對象,這就是代理模式。

可經過github源碼進行實操練習。

但願能對你有所幫助,感謝閱讀~別忘了點個贊鼓勵一下我哦,筆芯❤️


· 往期精彩 ·

【設計模式-誰沒碰見過幾個單例模式(一)】

【設計模式-什麼是快樂星球,什麼是策略模式(二)】

【設計模式-原來這就是代理模式(三)】

【設計模式-簡單易懂的觀察者模式(四)】

【設計模式-不會吧,不會還有人不知道裝飾器模式吧(五)】

相關文章
相關標籤/搜索