javascript設(shè)計模式之代理模式
一. 初識代理模式
代理模式是為一個對象提供一個代用品或占位符,以便控制對它的訪問。它的用處就是當(dāng)客戶不方便直接訪問一個對象或者不滿足需要的時候,提供一個替身對象來控制對這個對象的訪問。通俗來講就是,代理是一個中間人,負(fù)責(zé)在客戶和賣家之間傳遞信息,將沒用的信息過濾,將有利于交易成功的信息傳遞給賣家,從而加大交易的成功率。
二. 代理模式的實現(xiàn)思想
現(xiàn)在我們來通過一個小明給女神送花的例子來實現(xiàn)代理模式。
沒有使用代理模式
let Flower = function () {};
let xiaomingFirst = {
sendFlower ( target ) {
let flower = new Flower();
target.receiveFlower( flower );
}
}
let A = {
receiveFlower ( flower ) {
console.log('收到花')
}
}
xiaomingFirst.sendFlower(A);使用代理模式重構(gòu)
設(shè)定一個需求:AA 為小明的女神,AA 在心情好的時候接受小明花的幾率更大,而 B 是 AA 和小明的好朋友,因此小明將花送給 B,讓 B 在 AA 心情好時轉(zhuǎn)告自己的心意。
分析:重構(gòu)后的代碼增加了一個新的對象 B , 此時 B 為 AA 的代理,B 監(jiān)聽 AA 的好心情,代碼中假定5秒后 AA 心情變好,B 將花送出。
let xiaomingSencend = {
sendFlower ( target ) {
let flower = new Flower();
target.receiveFlower( flower );
}
}
let AA = {
receiveFlower () {
console.log('收到花');
},
listenGoodMood ( fn ) {
setTimeout(()=>{
fn();
}, 5000);
}
}
let B = {
receiveFlower ( flower ) {
AA.listenGoodMood( ()=>{
AA.receiveFlower( flower );
} );
}
}
xiaomingSencend.sendFlower(B);三. 代理模式分類
上述代碼中代理模式可能顯得不那么重要,但是體現(xiàn)了代理的思想,假如女神有一些要求,給他送花的男生必須帥而有錢,但又不能顯得那么勢力,因此代理可以幫其過濾掉這些不符合要求的男生,減少自己的許多麻煩,還能保持自己的美好形象這就是代理的用處。再者說,買一朵花很昂貴,而不是單單 new 這么簡單,男生不想浪費自己的錢,因此想先確定女神是否接受自己的花,便讓代理幫忙詢問,如果女神接受則讓代理幫忙買一朵送給女神,這樣減少了男生的損失但也達(dá)成了目的,這便引出了以下兩種代理模式。
- 保護(hù)代理:本體的要求直接在代理中實現(xiàn),過濾掉不符合的要求的訪問者對本體的請求
- 虛擬代理:將一些開銷很大的操作等到準(zhǔn)備向本體請求時候再實現(xiàn)(主要用于實際開發(fā))
四. 虛擬代理模式的實際運用
1. 虛擬代理實現(xiàn)圖片預(yù)加載
分析:先加載本地圖片,然后開始發(fā)起請求獲取圖片,當(dāng)圖片加載獲取成功后,再調(diào)用 myImage 將圖片替換預(yù)加載時顯示的圖片。
let myImage = function () {
let img = document.createElement('img');
document.body.appendChild(img);
return {
setSrc ( src ) {
img.src = src;
}
}
}();
proxyImage = function () {
let img = new Image;
img.onload = function () {
myImage.setSrc( this.src );
}
return {
setProxyImage( src ) {
myImage.setSrc("https://img.zcool.cn/community/01e2115d5d5c7da80120695c137bfb.jpg@1280w_1l_2o_100sh.jpg"); // 此處為加載 loading 圖片,用來占位,本地圖片(作者使用網(wǎng)絡(luò)圖片)
img.src = src;
}
}
}();
proxyImage.setProxyImage("https://img.zcool.cn/community/01a5d45543cd170000019ae94fc087.jpg@1280w_1l_2o_100sh.jpg");2. 緩存代理
分析:在代理中將本體計算的結(jié)果進(jìn)行緩存,如果下次再遇到同樣的請求,直接從緩存中獲取,減少對本體的訪問,減少性能消耗。
let myImage = function () {
let img = document.createElement('img');
document.body.appendChild(img);
return {
setSrc ( src ) {
img.src = src;
}
}
}();
proxyImage = function () {
let img = new Image;
img.onload = function () {
myImage.setSrc( this.src );
}
return {
setProxyImage( src ) {
myImage.setSrc("https://img.zcool.cn/community/01e2115d5d5c7da80120695c137bfb.jpg@1280w_1l_2o_100sh.jpg"); // 此處為加載 loading 圖片,用來占位,本地圖片(作者使用網(wǎng)絡(luò)圖片)
img.src = src;
}
}
}();
proxyImage.setProxyImage("https://img.zcool.cn/community/01a5d45543cd170000019ae94fc087.jpg@1280w_1l_2o_100sh.jpg");3. 虛擬代理合并 Http 請求
使用場景:點擊復(fù)選框向服務(wù)器發(fā)起請求同步文件,每次點擊復(fù)選框便發(fā)起一次請求,造成巨大網(wǎng)絡(luò)開銷,此時我們優(yōu)化的方式為,將想同步的文件緩存下來,在 3 秒后通過一次請求發(fā)送到服務(wù)器。
<input type="checkbox" >1
<input type="checkbox" >2
<input type="checkbox" >3
<input type="checkbox" >4
<input type="checkbox" >5
<input type="checkbox" >6
<input type="checkbox" >7
<input type="checkbox" >8
<script>
// 同步文件
let synchronousFile = function ( id ) {
console.log("開始同步文件" + id);
}
// 代理實現(xiàn)同步文件
let proxySynchronousFile = function ( ) {
let cache = [];
let time;
return function ( id ) {
cache.push( id );
if( time ) {
return;
}
time = setInterval(() => {
synchronousFile(cache.join(','));
clearInterval(time);
time = null;
cache.length = 0;
}, 3000);
}
}()
// 添加點擊執(zhí)行
let checkboxList = document.getElementsByTagName('input');
for(let i = 0, l = checkboxList.length; i < l; i++) {
checkboxList[i].onclick = function() {
if( this.checked === true ) {
proxySynchronousFile(i+1);
}
}
}五. 代理的使用意義及要求
- 意義:首先我們引入面向?qū)ο笤O(shè)計原則,單一職責(zé)原則,一個對象應(yīng)該盡可能少的承擔(dān)職責(zé),最好是一個,如果承擔(dān)職責(zé)過多,會提高代碼的耦合度,從而導(dǎo)致脆弱和低內(nèi)聚的設(shè)計。當(dāng)變化發(fā)生,設(shè)計可能遭到意外破壞,即使實現(xiàn)同樣需求可以將代碼封裝到一個函數(shù)中,但是最好的處理措施是引入代理模式
- 要求:代理和本體接口要一致,在任何使用本體地方都可以用代理來代替
六. 總結(jié)
代理模式包括很多小模塊,最常用的為緩存代理 和 虛擬代理,雖然代理模式非常有用,但我們再編寫業(yè)務(wù)代碼時不需要預(yù)先猜測是否需要使用代理模式,到發(fā)現(xiàn)不方便直接訪問某個對象,真正使用時再編寫也不遲。
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
JavaScript中使用concat()方法拼接字符串的教程
這篇文章主要介紹了JavaScript中使用concat()方法拼接字符串的教程,是JS入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下2015-06-06
JS實現(xiàn)的生成隨機(jī)數(shù)的4個函數(shù)分享
這篇文章主要介紹了JS實現(xiàn)的生成隨機(jī)數(shù)的4個函數(shù)分享,本文直接給出實現(xiàn)代碼,需要的朋友可以參考下2015-02-02
詳解在網(wǎng)頁上通過JS實現(xiàn)文本的語音朗讀
這篇文章主要介紹了在網(wǎng)頁上通過JS實現(xiàn)文本的語音朗讀,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03
javaScript面向?qū)ο罄^承方法經(jīng)典實現(xiàn)
很多人都說JavaScript不能算是面向?qū)ο蟮淖兂烧Z言。但是JavaScript的類型非常松散,也沒有編譯器,但是我們可以模仿著其他語言實現(xiàn)面向?qū)ο蟮姆绞絹韺崿F(xiàn)JavaScript的面向編程2013-08-08
JavaScript字符串對象toUpperCase方法入門實例(用于把字母轉(zhuǎn)換為大寫)
這篇文章主要介紹了JavaScript字符串對象toUpperCase方法入門實例,toUpperCase方法用于把字母轉(zhuǎn)換為大寫,需要的朋友可以參考下2014-10-10
Javascript中 關(guān)于prototype屬性實現(xiàn)繼承的原理圖
Javascript中關(guān)于prototype屬性實現(xiàn)繼承的原理圖2013-04-04

