代理模式
代理是一個對象,它可以用來控制對本體對象的訪問,它與本體對象實現(xiàn)了同樣的接口,代理對象會把所有的調(diào)用方法傳遞給本體對象的;代理模式最基本的形式是對訪問進行控制,而本體對象則負(fù)責(zé)執(zhí)行所分派的那個對象的函數(shù)或者類,簡單的來講本地對象注重的去執(zhí)行頁面上的代碼,代理則控制本地對象何時被實例化,何時被使用;我們在上面的單體模式中使用過一些代理模式,就是使用代理模式實現(xiàn)單體模式的實例化,其他的事情就交給本體對象去處理;
代理的優(yōu)點:
代理對象可以代替本體被實例化,并使其可以被遠(yuǎn)程訪問;
它還可以把本體實例化推遲到真正需要的時候;對于實例化比較費時的本體對象,或者因為尺寸比較大以至于不用時不適于保存在內(nèi)存中的本體,我們可以推遲實例化該對象;
1、我們先來理解代理對象代替本體對象被實例化的列子;
比如現(xiàn)在京東ceo想送給奶茶妹一個禮物,但是呢假如該ceo不好意思送,或者由于工作忙沒有時間送,那么這個時候他就想委托他的經(jīng)紀(jì)人去做這件事,于是我們可以使用代理模式來編寫如下代碼:
// 先申明一個奶茶妹對象
var TeaAndMilkGirl = function(name) {
this.name = name;
};
// 這是京東ceo先生
var Ceo = function(girl) {
this.girl = girl;
// 送結(jié)婚禮物 給奶茶妹
this.sendMarriageRing = function(ring) {
console.log("Hi " + this.girl.name + ", ceo送你一個禮物:" + ring);
}
};
// 京東ceo的經(jīng)紀(jì)人是代理,來代替送
var ProxyObj = function(girl){
this.girl = girl;
// 經(jīng)紀(jì)人代理送禮物給奶茶妹
this.sendGift = function(gift) {
// 代理模式負(fù)責(zé)本體對象實例化
(new Ceo(this.girl)).sendMarriageRing(gift);
}
};
// 初始化
var proxy = new ProxyObj(new TeaAndMilkGirl("奶茶妹"));
proxy.sendGift("結(jié)婚戒"); // Hi 奶茶妹, ceo送你一個禮物:結(jié)婚戒
代碼如上的基本結(jié)構(gòu),TeaAndMilkGirl是一個被送的對象(這里是奶茶妹);Ceo是送禮物的對象,他保存了奶茶妹這個屬性,及有一個自己的特權(quán)方法sendMarriageRing就是送禮物給奶茶妹這么一個方法;然后呢他是想通過他的經(jīng)紀(jì)人去把這件事完成,于是需要創(chuàng)建一個經(jīng)濟人的代理模式,名字叫ProxyObj;他的主要做的事情是,把ceo交給他的禮物送給ceo的情人,因此該對象同樣需要保存ceo情人的對象作為自己的屬性,同時也需要一個特權(quán)方法sendGift,該方法是送禮物,因此在該方法內(nèi)可以實例化本體對象,這里的本體對象是ceo送花這件事情,因此需要實例化該本體對象后及調(diào)用本體對象的方法(sendMarriageRing).
最后我們初始化是需要代理對象ProxyObj;調(diào)用ProxyObj對象的送花這個方法(sendGift)即可;
對于我們提到的優(yōu)點,第二點的話,我們下面可以來理解下虛擬代理,虛擬代理用于控制對那種創(chuàng)建開銷很大的本體訪問,它會把本體的實例化推遲到有方法被調(diào)用的時候;比如說現(xiàn)在有一個對象的實例化很慢的話,不能在網(wǎng)頁加載的時候立即完成,我們可以為其創(chuàng)建一個虛擬代理,讓他把該對象的實例推遲到需要的時候。
2、理解使用虛擬代理實現(xiàn)圖片的預(yù)加載
在網(wǎng)頁開發(fā)中,圖片的預(yù)加載是一種比較常用的技術(shù),如果直接給img標(biāo)簽節(jié)點設(shè)置src屬性的話,如果圖片比較大的話,或者網(wǎng)速相對比較慢的話,那么在圖片未加載完之前,圖片會有一段時間是空白的場景,這樣對于用戶體驗來講并不好,那么這個時候我們可以在圖片未加載完之前我們可以使用一個loading加載圖片來作為一個占位符,來提示用戶該圖片正在加載,等圖片加載完后我們可以對該圖片直接進行賦值即可;下面我們先不用代理模式來實現(xiàn)圖片的預(yù)加載的情況下代碼如下:
第一種方案:不使用代理的預(yù)加載圖片函數(shù)如下
// 不使用代理的預(yù)加載圖片函數(shù)如下
var myImage = (function() {
var imgNode = document.createElement("img");
document.body.appendChild(imgNode);
var img = new Image();
img.onload = function() {
imgNode.src = this.src;
};
return {
setSrc: function(src) {
imgNode.src = "img/g.jpg"
img.src = src;
}
}
})();
// 調(diào)用方式
myImage.setSrc("http://img1.imgtn.bdimg.com/i=yxt.jpg");
如上代碼是不使用代理模式來實現(xiàn)的代碼;
第二種方案:使用代理模式來編寫預(yù)加載圖片的代碼如下:
var myImage = (function() {
var imgNode = document.createElement("img");
document.body.appendChild(imgNode);
return {
setSrc: function(src) {
imgNode.src = src;
}
}
})();
// 代理模式
var ProxyImage = (function() {
var img = new Image();
img.onload = function() {
myImage.setSrc(this.src);
};
return {
setSrc: function(src) {
myImage.setSrc("img/g.jpg");
img.src = src;
}
}
})();
// 調(diào)用方式
ProxyImage.setSrc("http://img1.imgtn.bdimg.com/i=yxt.jpg");
第一種方案是使用一般的編碼方式實現(xiàn)圖片的預(yù)加載技術(shù),首先創(chuàng)建imgNode元素,然后調(diào)用myImage.setSrc該方法的時候,先給圖片一個預(yù)加載圖片,當(dāng)圖片加載完的時候,再給img元素賦值,第二種方案是使用代理模式來實現(xiàn)的,myImage函數(shù)只負(fù)責(zé)創(chuàng)建img元素,代理函數(shù)ProxyImage負(fù)責(zé)給圖片設(shè)置loading圖片,當(dāng)圖片真正加載完后的話,調(diào)用myImage中的myImage.setSrc方法設(shè)置圖片的路徑;
他們之間的 優(yōu)缺點如下:
第一種方案一般的方法代碼的耦合性太高,一個函數(shù)內(nèi)負(fù)責(zé)做了幾件事情,比如創(chuàng)建img元素,和實現(xiàn)給未加載圖片完成之前設(shè)置loading加載狀態(tài)等多項事情,未滿足面向?qū)ο笤O(shè)計原則中單一職責(zé)原則;并且當(dāng)某個時候不需要代理的時候,需要從myImage函數(shù)內(nèi)把代碼刪掉,這樣代碼耦合性太高。
第二種方案使用代理模式,其中myImage函數(shù)只負(fù)責(zé)做一件事,創(chuàng)建img元素加入到頁面中,其中的加載loading圖片交給代理函數(shù)ProxyImage去做,當(dāng)圖片加載成功后,代理函數(shù)ProxyImage會通知及執(zhí)行myImage函數(shù)的方法,同時當(dāng)以后不需要代理對象的話,我們直接可以調(diào)用本體對象的方法即可;
從上面代理模式我們可以看到,代理模式和本體對象中有相同的方法setSrc,這樣設(shè)置的話有如下2個優(yōu)點:
用戶可以放心地請求代理,他們只關(guān)心是否能得到想要的結(jié)果。假如我門不需要代理對象的話,直接可以換成本體對象調(diào)用該方法即可。
在任何使用本體對象的地方都可以替換成使用代理。
設(shè)計模式:http://www.cnblogs.com/tugenhua0707/p/5198407.html