JS設計模式四:代理模式

明易發表於2018-12-05

這裡有一份簡潔的前端知識體系等待你查收,看看吧,會有驚喜哦~如果覺得不錯,懇求star哈~


代理模式簡述

代理是一個物件,跟本體物件具有相同的介面,以此達到對本體物件的訪問控制。

簡單言之,本體物件只注重業務邏輯的實現,代理則控制本體物件的例項化(何時例項化、何時被使用)。

代理模式的優點在於:代理物件可以代替本體物件被例項化,此時本體物件未真正例項化,等到合適時機再例項化。

代理模式可以延遲建立開銷很大的本體物件,他會把本體的例項化推遲到有方法被呼叫時。

一個簡單的例子

// 宣告女孩物件
var girl = function (name) {
    this.name = name;
};
// 宣告男孩物件
var boy = function (girl) {
    this.girl = girl;
    this.sendGift = function (gift) {
        alert("Hi " + girl.name + ", 男孩送你一個禮物:" + gift);
    }
};
// 宣告代理物件
var proxyObj = function (girl) {
    this.girl = girl;
    this.sendGift = function (gift) {
        (new boy(girl)).sendGift(gift); // 替dudu送花咯
    }
};
var proxy = new proxyObj(new girl("花花"));
proxy.sendGift("999朵玫瑰");
複製程式碼

如上程式碼,girl是一個被送禮物的物件,boy是送禮物的物件,他儲存了girl這個屬性,還有一個送禮物的方法sendGift,然後他通過proxyObj去完成這件事,proxyObj就是代理,他把boy的禮物送給了girl,因此proxyObj同樣需要儲存girl的屬性,同時也有sendGift方法,該方法例項化本體物件boy並呼叫了boy的sendGift方法,完成了boy送girl禮物的這個過程。

實戰一:圖片載入

在前端開發中,使用圖片是非常常見的場景,如果直接給img標籤設定src屬性,如果圖片過大,或網速比較慢,圖片在載入過程中會有一段時間的空白,使用者體驗不好。

傳統的解決方案

傳統的解決方法是:在圖片未載入完成之前,使用一個loading圖示作為佔位符,等圖片完成載入後,再使用真實的圖片地址替代loading圖示。如下:

// 不使用代理的預載入圖片函式如下
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 = "http://img.lanrentuku.com/img/allimg/1212/5-121204193R0.gif";
            img.src = src;
        }
    }
})();
// 呼叫方式
myImage.setSrc("https://www.baidu.com/img/bd_logo1.png");
複製程式碼

如上程式碼,這是使用一般的編碼方式實現圖片的預載入技術的方案,首先建立imgNode元素,然後呼叫myImage.setSrc該方法的時候,先給圖片一個預載入圖片,當圖片載入完的時候,再給img元素賦值。

這種方案是可以實現功能,但也有比較明顯的缺陷:耦合性太高,myImage函式違背了物件導向設計原則中的單一職責原則,同時完成了建立img,設定loading載入狀態等多個任務。

此時就可以使用代理模式來實現~~~

代理模式解決方案

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("http://img.lanrentuku.com/img/allimg/1212/5-121204193R0.gif");
        img.src = src;
        }
    }
})();
// 呼叫方式
ProxyImage.setSrc("https://www.baidu.com/img/bd_logo1.png");
複製程式碼

如上程式碼,myImage 函式只負責做一件事,建立img元素加入到頁面中,其中的載入loading圖片交給代理函式ProxyImage 去做,當圖片載入成功後,代理函式ProxyImage 會通知及執行myImage 函式的方法,同時當以後不需要代理物件的話,我們直接可以呼叫本體物件的方法即可。

代理模式跟本體物件具有相同的對外介面,有兩個好處: 一、使用者可以放心地請求代理,不需要了解代理的實現過程,只要結果符合預期即可。如果不需要代理物件了,可以換成呼叫本體物件的該方法 二、在任何使用本體物件的地方,都可以使用代理替換。

最後,強調一點,主體物件跟代理物件也可以都返回一個匿名函式,這樣也認為他們具有相同的介面。

實戰二:快取代理

對第一次執行的結果進行快取,當再一次執行相同運算的時候,直接從快取裡面取,避免重複運算,如果運算非常複雜的話,對效能很耗費,那麼使用快取物件可以提高效能。以下是一個簡單的例子:

var mult = function(){
    var a = 1;
    for(var i = 0,ilen = arguments.length; i < ilen; i+=1) {
        a = a*arguments[i];
    }
    return a;
};
// 計算加法
var plus = function(){
    var a = 0;
    for(var i = 0,ilen = arguments.length; i < ilen; i+=1) {
        a += arguments[i];
    }
    return a;
}
// 代理函式
var proxyFunc = function(fn) {
    var cache = {};  // 快取物件
    return function(){
        var args = Array.prototype.join.call(arguments,',');
        if(args in cache) {
            return cache[args];   // 使用快取代理
        }
        return cache[args] = fn.apply(this,arguments);
    }
};
var proxyMult = proxyFunc(mult);
console.log(proxyMult(1,2,3,4)); // 24
console.log(proxyMult(1,2,3,4)); // 快取取 24

var proxyPlus = proxyFunc(plus);
console.log(proxyPlus(1,2,3,4));  // 10
console.log(proxyPlus(1,2,3,4));  // 快取取 10
複製程式碼

如上程式碼就是網上常見的加法和乘法的運算。通過快取代理,可以減少不必要的運算量。

相關文章