所謂的的代理模式就是為一個物件找一個替代物件,以便對原物件進行訪問。
使用代理的原因是我們不願意或者不想對原物件進行直接操作,我們使用代理就是讓它幫原物件進行一系列的操作,等這些東西做完後告訴原物件就行了。就像我們生活的那些明星的助理經紀人一樣。
我們舉一個明星買鞋子的例子。
1.明星自己去買鞋。
// 定義一個鞋子類
var Shoes = function(name){
this.name = name;
};
Shoes.prototype.getName = function() {
return this.name;
};
// 定義一個明星物件
var star = {
buyShoes: function(shoes) {
console.log('買到了一雙' + shoes.getName());
}
}
star.buyShoes(new Shoes('皮鞋')); // "買到了一雙皮鞋"
複製程式碼
當然了,想買鞋這種事,一般都會交給助理去做。
2.明星讓助理代自己去買鞋。
// 定義一個鞋子類
var Shoes = function(name){
this.name = name;
};
Shoes.prototype.getName = function() {
return this.name;
};
// 定義一個助理物件
var assistant = {
buyShoes: function(shoes) {
star.buyShoes(shoes.getName())
}
};
// 定義一個明星物件
var star = {
buyShoes: function(name) {
console.log('買到了一雙' + name);
}
};
assistant.buyShoes(new Shoes('高跟鞋')); // "買到了一雙高跟鞋"
複製程式碼
怎麼樣,代理就是這麼簡單,可能到這裡有的同學會比較疑惑,代理的實現結果不是和不使用一樣嗎?是的,一樣的實現結果是必須的,但是,值用代理並不是我們看到的那樣將簡單的事情複雜化了,代理的使用場景當然不是這種簡單的場景,而是針對一些比較複雜或特殊的情況使用,這裡只是為了舉例說明代理的實現。下面就介紹一些使用場景。
代理使用場景
繼續上面的明星買鞋子的問題。在生活中我們會遇到商店在營業時間,而你在工作時間,由於要掙錢同時又要花錢,所以,會找一個代理;就像春節快到了,你沒時間或者搶不到票,就會找票販子一樣;像現在的代購,則是你不能出國,或者對國外不瞭解,就找能出國,對國外瞭解的人幫你買東西一樣。我們知道每家商店都有自己的營業時間和休息時間,這裡我們用(8:00~20:00)算作營業時間。
// 定義一個鞋子類
var Shoes = function(name){
this.name = name;
};
Shoes.prototype.getName = function() {
return this.name;
};
// 新增了一個business方法,通過當前的時間來判斷是否能買到鞋子。
Shoes.prototype.business = function() {
var curTime = new Date().getHours();
return curTime >= 8 && curTime <= 20 ? that.getName() : '"非營業時間!"';
}
// 定義一個助理物件
var assistant = {
buyShoes: function(shoes) {
star.buyShoes(shoes.getName())
}
};
// 定義一個明星物件
var star = {
buyShoes: function(name) {
console.log('買到了一雙' + name);
}
};
assistant.buyShoes(new Shoes('高跟鞋')); // "買到了一雙高跟鞋"
複製程式碼
保護代理
助理作為明星的代理,不僅可以幫助明星買東西,同時還有幫助明星過濾的東西的職責,比如說,有粉絲要送明星花(不是什麼樣的花都收的),有人要找明星代言廣告(不是什麼樣的廣告都代言的)。
// 定義一個廣告類
var Ad = function(price){
this.price = price;
};
Ad.prototype.getPrice = function() {
return this.price;
};
// 定義一個助理物件
var assistant = {
init: function(ad) {
var money = ad.getPrice();
if(money > 300) {
this.receiveAd(money);
} else {
this.rejectAd();
}
},
receiveAd: function(price) {
star.receiveAd(price);
},
rejectAd: function() {
star.rejectAd();
}
};
// 定義一個明星物件
var star = {
receiveAd: function(price) {
console.log('廣告費' + price + '萬元');
},
rejectAd: function() {
console.log('拒絕小製作!');
}
};
assistant.init(new Ad(5)); // "拒絕小製作!"
assistant.init(new Ad(500)); // "廣告費500萬元"
複製程式碼
像這種明星向助理授權,如:什麼樣價位的廣告可以接,什麼樣的鮮花可以接等等。這樣將一些業務的處理交給助理或者經紀人處理,而自己則位於幕後,無疑給自己減少了不必要的麻煩,這樣明星就處於一種保護狀態。在現實生活中的例子比比皆是,同樣在我們的程式語言開發中也是比較常見,尤其是網路和程式這方面,相信做過nodjs開發的同學或多或少會遇到。
虛擬代理
在開發中,我們往往將 new Ad('5')
這個物件的例項化操作,放到函式內部執行,這樣的操作會減少不必要的例項化物件的開銷,造成資源的浪費。這種使用的情況我們將之成為虛擬代理。
下面就介紹一個常見的虛擬代理——圖片的預載入。
圖片預載入是一種常見的前端技術,由於圖片過大或者網路不佳,我們不會直接給某個img標籤節點設定src屬性,而是使用一張loading圖片作為佔位符,然後用非同步的方式來家在載入圖片,等到圖片載入完畢,我們再把它填充到img的節點裡。
var preImage = (function() {
var imgNode = document.createElement('img');
document.body.appendChild(imgNode);
var img = new Image;
img.onload = function() {
imgNode.src = img.src;
};
return {
setSrc: function(src) {
imgNode.src = '../loading.gif';
img.src = src;
}
}
})();
preImage.setSrc('https://cn.bing.com/az/hprichbg/rb/TadamiTrain_ZH-CN13495442975_1920x1080.jpg');
複製程式碼
到這裡,圖片的預載入功能已經實現,但是往往體現一段程式碼的是否更優秀,是看你的程式碼是否易於維護,特別是對於海量的程式碼。第一點,這段程式碼不符合單一職責,我們把負責圖片預載入的功能,對img元素的處理放在了一個函式體內,尤其是沒有將程式碼變化的部分和未變化的部分分開;第二點,就是將來我們的網速非常好,我們不用再擔心由於網路不佳而造成的顯示效果問題,那麼關於圖片預載入的功能就要去掉。
我們可以嘗試使用代理來實現,程式碼如下:
var myImage = (function() {
var imgNode = document.createElement('img');
document.body.appendChild(imgNode);
return {
setSrc: function(src) {
imgNode.src = src;
}
}
})();
var preImage = (function() {
var img = new Image;
img.onload = function() {
myImage.setSrc = img.src;
};
return {
setSrc: function(src) {
myImage.setSrc('../loading.gif');
img.src = src;
}
}
})();
preImage.setSrc('https://cn.bing.com/az/hprichbg/rb/TadamiTrain_ZH-CN13495442975_1920x1080.jpg');
複製程式碼
這樣我們就將圖片預載入和為img元素節點設定src分開來。
代理和被代理物件的一致性
因為代理要實現和被代理物件實際處理一樣的效果,所以,在實現代理物件時,原物件有的方法,代理物件一樣有,這樣可以保證,使用者在操作代理物件時就像在操作原物件一樣。
快取代理
快取代理就是將代理加快取,下面是一個求和的例子:
var multAdd = function() {
var res = 0;
for (var i = 0, l = arguments.length; i < l; i++) {
res = res + arguments[i]
}
return res;
};
var proxyAdd = (function() {
var cache = {};
return function() {
var args = Array.prototype.join.call(arguments, ',');
if(args in cache) {
return cache[args];
}
return caches[args] = multAdd.apply(this, arguments);
}
})();
proxyAdd(1, 2, 3); // 6
proxyAdd(1, 2, 3); // 6
複製程式碼
我們不用代理當然也能實現快取就和,但是為了達到單一職責,我們可以讓multAdd實現求和,而快取則放在代理中來實現。
當然,還有其他的分類代理,比如,智慧代理,遠端代理。但是在JavaScript中我們使用最多,也最常見的就是虛擬代理和快取代理。