国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

JavaScript 設計模式(三):代理模式

Keagan / 605人閱讀

摘要:虛擬代理延遲執(zhí)行虛擬代理的目的,是將開銷大的運算延遲到需要時再執(zhí)行。

代理模式:為一個對象提供一個代用品或占位符,以便控制它的訪問。

當我們不方便直接訪問某個對象時,或不滿足需求時,可考慮使用一個替身對象來控制該對象的訪問。替身對象可對請求預先進行處理,再決定是否轉交給本體對象。

生活小栗子:

代購;

明星經(jīng)紀人;

和諧上網(wǎng)

經(jīng)常 shopping 的同學,對代購應該不陌生。自己不方便直接購買或買不到某件商品時,會選擇委托給第三方,讓代購或黃牛去做購買動作。程序世界的代理者也是如此,我們不直接操作原有對象,而是委托代理者去進行。代理者的作用,就是對我們的請求預先進行處理或轉接給實際對象。

模式特點

代理對象可預先處理請求,再決定是否轉交給本體;

代理和本體對外顯示接口保持一致性

代理對象僅對本體做一次包裝

模式細分

虛擬代理(將開銷大的運算延遲到需要時執(zhí)行)

緩存代理(為開銷大的運算結果提供緩存)

保護代理(黑白雙簧,代理充當黑臉,攔截非分要求)

防火墻代理(控制網(wǎng)絡資源的訪問)

遠程代理(為一個對象在不同的地址控件提供局部代表)

智能引用代理(訪問對象執(zhí)行一些附加操作)

寫時復制代理(延遲對象復制過程,對象需要真正修改時才進行)

JavaScript 中常用的代理模式為 “虛擬代理” 和 “緩存代理”。

模式實現(xiàn)
實現(xiàn)方式:創(chuàng)建一個代理對象,代理對象可預先對請求進行處理,再決定是否轉交給本體,代理和本體對外接口保持一致性(接口名相同)。
// 例子:代理接聽電話,實現(xiàn)攔截黑名單
var backPhoneList = ["189XXXXX140"];       // 黑名單列表
// 代理
var ProxyAcceptPhone = function(phone) {
    // 預處理
    console.log("電話正在接入...");
    if (backPhoneList.includes(phone)) {
        // 屏蔽
        console.log("屏蔽黑名單電話");
    } else {
        // 轉接
        AcceptPhone.call(this, phone);
    }
}
// 本體
var AcceptPhone = function(phone) {
    console.log("接聽電話:", phone);
};

// 外部調用代理
ProxyAcceptPhone("189XXXXX140"); 
ProxyAcceptPhone("189XXXXX141"); 

代理并不會改變本體對象,遵循 “單一職責原則”,即 “自掃門前雪,各找各家”。不同對象承擔獨立職責,不過于緊密耦合,具體執(zhí)行功能還是本體對象,只是引入代理可以選擇性地預先處理請求。例如上述代碼中,我們向 “接聽電話功能” 本體添加了一個屏蔽黑名單的功能(保護代理),預先處理電話接入請求。

虛擬代理(延遲執(zhí)行)

虛擬代理的目的,是將開銷大的運算延遲到需要時再執(zhí)行。

虛擬代理在圖片預加載的應用,代碼例子來至 《JavaScript 設計模式與開發(fā)實踐》

// 本體
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);             // 圖片加載完設置真實圖片src
    }
    return {
        setSrc: function(src) {
            myImage.setSrc("./loading.gif");  // 預先設置圖片src為loading圖
            img.src = src;
        }
    }
})();

// 外部調用
proxyImage.setSrc("./product.png");           // 有l(wèi)oading圖的圖片預加載效果
緩存代理(暫時存儲)

緩存代理的目的,是為一些開銷大的運算結果提供暫時存儲,以便下次調用時,參數(shù)與結果不變情況下,從緩存返回結果,而不是重新進行本體運算,減少本體調用次數(shù)。

應用緩存代理的本體,要求運算函數(shù)應是一個純函數(shù),簡單理解比如一個求和函數(shù) sum, 輸入?yún)?shù) (1, 1), 得到的結果應該永遠是 2

純函數(shù):固定的輸入,有固定的輸出,不影響外部數(shù)據(jù)。

模擬場景:60道判斷題測試,每三道題計分一次,根據(jù)計分篩選下一步的三道題目?

三道判斷題得分結果:

(0, 0 ,0)

(0, 0, 1)

(0, 1, 0)

(0, 1, 1)

(1, 0, 0)

(1, 0, 1)

(1, 1, 0)

(1, 1, 1)

總共七種計分結果。60/3 = 20,共進行 20 次計分,每次計分執(zhí)行 3 個循環(huán)累計,共 60 個循環(huán)。接下來,借用 “緩存代理” 方式,來實現(xiàn)最少本體運算次數(shù)。

// 本體:對三道題答案進行計分
var countScore = function(ansList) {
    let [a, b, c] = ansList;
    return a + b + c;
}

// 代理:對計分請求預先處理
var proxyCountScore = (function() {
    var existScore = {};    // 設定存儲對象
    return function(ansList) {
        var attr = ansList.join(",");  // eg. ["0,0,0"]
        if (existScore[attr] != null) {
            // 從內(nèi)存返回
            return existScore[attr];
        } else {
            // 內(nèi)存不存在,轉交本體計算并存入內(nèi)存
            return existScore[attr] = countScore(ansList);
        }
    }
})();

// 調用計分
proxyCountScore([0,1,0]);

60 道題目,每 3 道題一次計分,共 20 次計分運算,但總的計分結果只有 7 種,那么實際上本體 countScore() 最多只需運算 7 次,即可囊括所有計算結果。

通過緩存代理的方式,對計分結果進行臨時存儲。用答案字符串組成屬性名 ["0,1,0"] 作為 key 值檢索內(nèi)存,若存在直接從內(nèi)存返回,減少包含復雜運算的本體被調用的次數(shù)。之后如果我們的題目增加至 90 道, 120 道,150 道題時,本體 countScore() 運算次數(shù)仍舊保持 7 次,中間節(jié)省了復雜運算的開銷。

ES6 的 Proxy

ES6新增的 Proxy 代理對象的操作,具體的實現(xiàn)方式是在 handler 上定義對象自定義方法集合,以便預先管控對象的操作。

ES6 的 Proxy語法:let proxyObj = new Proxy(target, handler);

target: 本體,要代理的對象

handler: 自定義操作方法集合

proxyObj: 返回的代理對象,擁有本體的方法,不過會被 handler 預處理

// ES6的Proxy
let Person = {
    name: "以樂之名"
};

const ProxyPerson = new Proxy(Person, {
    get(target, key, value) {
        if (key != "age") {
            return target[key];
        } else {
            return "保密"
        }
    },
    set(target, key, value) {
        if (key === "rate") {
            target[key] = value === "A" ? "推薦" : "待提高"
        }
    }
})

console.log(ProxyPerson.name);  // "以樂之名"
console.log(ProxyPerson.age);   // "保密"
ProxyPerson.rate = "A";         
console.log(ProxyPerson.rate);  // "推薦"
ProxyPerson.rate = "B";         
console.log(ProxyPerson.rate);  // "待提高"

handler 除常用的 set/get,總共支持 13 種方法:

handler.getPrototypeOf()
// 在讀取代理對象的原型時觸發(fā)該操作,比如在執(zhí)行 Object.getPrototypeOf(proxy) 時

handler.setPrototypeOf()
// 在設置代理對象的原型時觸發(fā)該操作,比如在執(zhí)行 Object.setPrototypeOf(proxy, null) 時

handler.isExtensible()
// 在判斷一個代理對象是否是可擴展時觸發(fā)該操作,比如在執(zhí)行 Object.isExtensible(proxy) 時

handler.preventExtensions()
// 在讓一個代理對象不可擴展時觸發(fā)該操作,比如在執(zhí)行 Object.preventExtensions(proxy) 時

handler.getOwnPropertyDescriptor()
// 在獲取代理對象某個屬性的屬性描述時觸發(fā)該操作,比如在執(zhí)行 Object.getOwnPropertyDescriptor(proxy, "foo") 時

handler.defineProperty()
// 在定義代理對象某個屬性時的屬性描述時觸發(fā)該操作,比如在執(zhí)行 Object.defineProperty(proxy, "foo", {}) 時

handler.has()
// 在判斷代理對象是否擁有某個屬性時觸發(fā)該操作,比如在執(zhí)行 "foo" in proxy 時

handler.get()
// 在讀取代理對象的某個屬性時觸發(fā)該操作,比如在執(zhí)行 proxy.foo 時

handler.set()
// 在給代理對象的某個屬性賦值時觸發(fā)該操作,比如在執(zhí)行 proxy.foo = 1 時

handler.deleteProperty()
// 在刪除代理對象的某個屬性時觸發(fā)該操作,比如在執(zhí)行 delete proxy.foo 時

handler.ownKeys()
// 在獲取代理對象的所有屬性鍵時觸發(fā)該操作,比如在執(zhí)行 Object.getOwnPropertyNames(proxy) 時

handler.apply()
// 在調用一個目標對象為函數(shù)的代理對象時觸發(fā)該操作,比如在執(zhí)行 proxy() 時。

handler.construct()
// 在給一個目標對象為構造函數(shù)的代理對象構造實例時觸發(fā)該操作,比如在執(zhí)行 new proxy() 時
適用場景

虛擬代理:

圖片預加載(loading 圖)

合并HTTP請求(數(shù)據(jù)上報匯總)

緩存代理:(前提本體是純函數(shù))

緩存異步請求數(shù)據(jù)

緩存較復雜的運算結果

ES6 的 Proxy:

實現(xiàn)對象私有屬性

實現(xiàn)表單驗證

“策略模式” 可應用于表單驗證信息,“代理方式” 也可實現(xiàn)。這里引用 Github - jawil 的一個例子,思路供大家分享。

// 利用 proxy 攔截格式不符數(shù)據(jù)
function validator(target, validator, errorMsg) {
    return new Proxy(target, {
        _validator: validator,
        set(target, key, value, proxy) {
            let errMsg = errorMsg;
            if (value == null || !value.length) {
                console.log(`${errMsg[key]} 不能為空`);
                return target[key] = false;
            }
            let va = this._validator[key];  // 這里有策略模式的應用
            if (!!va(value)) {
                return Reflect.set(target, key, value, proxy);
            } else {
                console.log(`${errMsg[key]} 格式不正確`);
                return target[key] = false;
            }
        }
    })
}

// 負責校驗的邏輯代碼
const validators = {
    name(value) {
        return value.length >= 6;
    },
    passwd(value) {
        return value.length >= 6;
    },
    moblie(value) {
        return /^1(3|5|7|8|9)[0-9]{9}$/.test(value);
    },
    email(value) {
        return /^w+([+-.]w+)*@w+([-.]w+)*.w+([-.]w+)*$/.test(value)
    }
}

// 調用代碼
const errorMsg = {
    name: "用戶名",
    passwd: "密碼",
    moblie: "手機號碼",
    email: "郵箱地址"
}
const vali = validator({}, validators, errorMsg)
let registerForm = document.querySelector("#registerForm")
registerForm.addEventListener("submit", function () {
    let validatorNext = function* () {
        yield vali.name = registerForm.userName.value
        yield vali.passwd = registerForm.passWord.value
        yield vali.moblie = registerForm.phone.value
        yield vali.email = registerForm.email.value
    }
    let validator = validatorNext();
    for (let field of validator) {
        validator.next();
    }
}

實現(xiàn)思路: 利用 ES6 的 proxy 自定義 handlerset() ,進行表單校驗并返回結果,并且借用 “策略模式" 獨立封裝驗證邏輯。使得表單對象,驗證邏輯,驗證器各自獨立。代碼整潔性,維護性及復用性都得到增強。

關于 “設計模式” 在表單驗證的應用,可參考 jawil 原文:《探索兩種優(yōu)雅的表單驗證——策略設計模式和ES6的Proxy代理模式》。

優(yōu)缺點

優(yōu)點:

可攔截和監(jiān)聽外部對本體對象的訪問;

復雜運算前可以進行校驗或資源管理;

對象職能粒度細分,函數(shù)功能復雜度降低,符合 “單一職責原則”;

依托代理,可額外添加擴展功能,而不修改本體對象,符合 “開發(fā)-封閉原則”

缺點:

額外代理對象的創(chuàng)建,增加部分內(nèi)存開銷;

處理請求速度可能有差別,非直接訪問存在開銷,但 “虛擬代理” 及 “緩存代理” 均能提升性能

參考文章

《JavaScript 設計模式與開發(fā)實踐》

《ES6中的代理模式-----Proxy》

《探索兩種優(yōu)雅的表單驗證——策略設計模式和ES6的Proxy代理模式》

Github,期待Star!
https://github.com/ZengLingYong/blog

作者:以樂之名
本文原創(chuàng),有不當?shù)牡胤綒g迎指出。轉載請指明出處。

文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/104978.html

相關文章

  • Javascript設計模式)單例模式

    摘要:單例模式的定義是保證一個類只有僅有一個實例,并提供一個訪問它的全局訪問點。并且按照單一職責原則類實現(xiàn)功能類管理單例管理單例模式,達到可組合的的效果創(chuàng)建普通類引入代理類惰性單例模式分離創(chuàng)建實例對象的職責與管理單例的職責。 單例模式的定義是:保證一個類只有僅有一個實例,并提供一個訪問它的全局訪問點。 單例模式是一種常用的模式,有些對象我們往往只需要一個,比如線程池,全局緩存,window對...

    孫吉亮 評論0 收藏0
  • 聽飛狐聊JavaScript設計模式系列09

    摘要:說白了,就是對訪問進行控制。這一回,主要聊了代理模式,虛擬代理,圖片懶加載,難度適中下一回,聊一下適配器模式,難度也比較小。 本回內(nèi)容介紹 上一回,聊了門面模式,DOM2級事件,事件委托,介一回,也比較容易,代理模式(proxy),代理對象控制對本體對象的訪問,實現(xiàn)了同樣的接口,并且會把任何方法的調用傳遞到本體對象。說白了,就是對訪問進行控制。直接上代碼,走你: 1.代理模式 代理也是...

    張春雷 評論0 收藏0
  • javascript單例、代理、狀態(tài)設計模式

    摘要:代理模式代理模式為一個對象提供一個代用品或占位符,以便控制對于它訪問。這種代理就叫虛擬代理。保護代理用于對象應該有不同訪問權限情況。寫時復制代理時虛擬代理的一種變體。 一、創(chuàng)建型設計模式(三大類設計模式) 創(chuàng)建型設計模式 --創(chuàng)建說明該類別里面的設計模式就是用來創(chuàng)建對象的,也就是在不同的場景下我們應該選用什么樣的方式來創(chuàng)建對象。 1. 單例模式 ==單例模式(Singleton)==:...

    0xE7A38A 評論0 收藏0
  • 談談前后端的分工協(xié)作

    摘要:針對上面看到的問題,現(xiàn)在也有一些團隊在嘗試前后端之間加一個中間層比如淘寶的。淘寶有很多類似的文章,這里不贅述。淘寶團隊做了兩套接口文檔的維護工具,以及,不知道有沒有對外開放,兩個東西都是基于的一個嘗試,各有優(yōu)劣。 原文出處: 小胡子哥的博客(@Barret李靖) 前后端分工協(xié)作是一個老生常談的大話題,很多公司都在嘗試用工程化的方式去提升前后端之間交流的效率,降低溝通成本,并且也開發(fā)了...

    Scholer 評論0 收藏0
  • 談談前后端的分工協(xié)作

    摘要:針對上面看到的問題,現(xiàn)在也有一些團隊在嘗試前后端之間加一個中間層比如淘寶的。淘寶有很多類似的文章,這里不贅述。淘寶團隊做了兩套接口文檔的維護工具,以及,不知道有沒有對外開放,兩個東西都是基于的一個嘗試,各有優(yōu)劣。 原文出處: 小胡子哥的博客(@Barret李靖) 前后端分工協(xié)作是一個老生常談的大話題,很多公司都在嘗試用工程化的方式去提升前后端之間交流的效率,降低溝通成本,并且也開發(fā)了...

    OBKoro1 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<