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

資訊專欄INFORMATION COLUMN

JavaScript 設(shè)計(jì)模式(三):代理模式

Keagan / 610人閱讀

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

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

當(dāng)我們不方便直接訪問某個(gè)對(duì)象時(shí),或不滿足需求時(shí),可考慮使用一個(gè)替身對(duì)象來控制該對(duì)象的訪問。替身對(duì)象可對(duì)請(qǐng)求預(yù)先進(jìn)行處理,再?zèng)Q定是否轉(zhuǎn)交給本體對(duì)象。

生活小栗子:

代購(gòu);

明星經(jīng)紀(jì)人;

和諧上網(wǎng)

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

模式特點(diǎn)

代理對(duì)象可預(yù)先處理請(qǐng)求,再?zèng)Q定是否轉(zhuǎn)交給本體;

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

代理對(duì)象僅對(duì)本體做一次包裝

模式細(xì)分

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

緩存代理(為開銷大的運(yùn)算結(jié)果提供緩存)

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

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

遠(yuǎn)程代理(為一個(gè)對(duì)象在不同的地址控件提供局部代表)

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

寫時(shí)復(fù)制代理(延遲對(duì)象復(fù)制過程,對(duì)象需要真正修改時(shí)才進(jìn)行)

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

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

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

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

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

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

虛擬代理在圖片預(yù)加載的應(yīng)用,代碼例子來至 《JavaScript 設(shè)計(jì)模式與開發(fā)實(shí)踐》

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

// 外部調(diào)用
proxyImage.setSrc("./product.png");           // 有l(wèi)oading圖的圖片預(yù)加載效果
緩存代理(暫時(shí)存儲(chǔ))

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

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

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

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

三道判斷題得分結(jié)果:

(0, 0 ,0)

(0, 0, 1)

(0, 1, 0)

(0, 1, 1)

(1, 0, 0)

(1, 0, 1)

(1, 1, 0)

(1, 1, 1)

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

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

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

// 調(diào)用計(jì)分
proxyCountScore([0,1,0]);

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

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

ES6 的 Proxy

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

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

target: 本體,要代理的對(duì)象

handler: 自定義操作方法集合

proxyObj: 返回的代理對(duì)象,擁有本體的方法,不過會(huì)被 handler 預(yù)處理

// 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()
// 在讀取代理對(duì)象的原型時(shí)觸發(fā)該操作,比如在執(zhí)行 Object.getPrototypeOf(proxy) 時(shí)

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

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

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

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

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

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

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

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

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

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

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

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

虛擬代理:

圖片預(yù)加載(loading 圖)

合并HTTP請(qǐng)求(數(shù)據(jù)上報(bào)匯總)

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

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

緩存較復(fù)雜的運(yùn)算結(jié)果

ES6 的 Proxy:

實(shí)現(xiàn)對(duì)象私有屬性

實(shí)現(xiàn)表單驗(yàn)證

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

// 利用 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];  // 這里有策略模式的應(yīng)用
            if (!!va(value)) {
                return Reflect.set(target, key, value, proxy);
            } else {
                console.log(`${errMsg[key]} 格式不正確`);
                return target[key] = false;
            }
        }
    })
}

// 負(fù)責(zé)校驗(yàn)的邏輯代碼
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)
    }
}

// 調(diào)用代碼
const errorMsg = {
    name: "用戶名",
    passwd: "密碼",
    moblie: "手機(jī)號(hào)碼",
    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();
    }
}

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

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

優(yōu)缺點(diǎn)

優(yōu)點(diǎn):

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

復(fù)雜運(yùn)算前可以進(jìn)行校驗(yàn)或資源管理;

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

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

缺點(diǎn):

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

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

參考文章

《JavaScript 設(shè)計(jì)模式與開發(fā)實(shí)踐》

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

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

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

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

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/104978.html

相關(guān)文章

  • Javascript設(shè)計(jì)模式)單例模式

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

    孫吉亮 評(píng)論0 收藏0
  • 聽飛狐聊JavaScript設(shè)計(jì)模式系列09

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

    張春雷 評(píng)論0 收藏0
  • javascript單例、代理、狀態(tài)設(shè)計(jì)模式

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

    0xE7A38A 評(píng)論0 收藏0
  • 談?wù)勄昂蠖说姆止f(xié)作

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

    Scholer 評(píng)論0 收藏0
  • 談?wù)勄昂蠖说姆止f(xié)作

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

    OBKoro1 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<