JavaScript 設(shè)計(jì)模式及代碼實(shí)現(xiàn)——代理模式

代理模式

1 定義

為其他對象提供一種代理以控制對這個(gè)對象的訪問

在某些情況下,一個(gè)對象不適合或者不能直接引用另一個(gè)對象井濒,而代理對象可以在客戶端和目標(biāo)對象之間起到中介的作用。

album_temp_1661991312

2 應(yīng)用舉例

2.1 緩存代理

現(xiàn)在我們有一個(gè)可以查詢城市經(jīng)緯度的函數(shù):

const getLatLng = (address) => {
  if (address === "Beijing") {
    return "北京經(jīng)緯度";
  } else if (address === "Hangzhou") {
    return "杭州經(jīng)緯度";
  } else if (address === "Shanghai") {
    return "上海經(jīng)緯度";
  } else if (address === "Nanjing") {
    return "南京經(jīng)緯度";
  } else {
    return "";
  }
};

如果我們多次查詢南京的經(jīng)緯度,每次都要經(jīng)過 4 次判斷矩屁,我們通過 getLatLngProxy 函數(shù)將查詢結(jié)果緩存下來抡柿,從而避免多次重復(fù)判斷

const getLatLngProxy = ((fn) => {
  const geoCache = {};
  return (address) => {
    console.log("緩存=" + geoCache[address]);
    return (geoCache[address] ??= fn(address));
  };
})(getLatLng);

getLatLngProxy("Nanjing"); // 緩存=undefined
getLatLngProxy("Nanjing"); // 緩存=南京經(jīng)緯度

4 次判斷看不出什么舔琅,但是如果 getLatLng 中的操作不是判斷,而是需要很復(fù)雜的計(jì)算洲劣,需要消耗很長時(shí)間备蚓,這時(shí)緩存的優(yōu)勢就很明顯了

我們在不修改原函數(shù)的前提下,通過高階函數(shù)創(chuàng)建了一個(gè)擁有緩存效果的代理函數(shù)

2.2 Vue2 響應(yīng)式原理——數(shù)據(jù)代理

如果你學(xué)習(xí)過 Vue2 響應(yīng)式原理囱稽,一定知道其中重要的一環(huán):數(shù)據(jù)代理郊尝。不知道也沒關(guān)系,下面舉個(gè)簡單的栗子來說明一下战惊。

const obj = {
  name: "JiMing",
};

let name = obj.name; // 訪問 obj.name
obj.name = "Ji"; // 修改 obj.name

假設(shè)現(xiàn)在有一個(gè)對象 obj流昏,如果我想在訪問或修改obj.name時(shí)做一些額外的操作,比如打印信息到控制臺(tái)吞获,該如何實(shí)現(xiàn)况凉?

JS 提供了 **Object.defineProperty()**方法,該方法可以在一個(gè)對象上定義一個(gè)新屬性各拷,或者修改一個(gè)對象的現(xiàn)有屬性刁绒,并返回此對象。

我們可以利用這個(gè) API 在代理對象上添加目標(biāo)對象的同名屬性烤黍,同時(shí)添加額外的操作

const proxyObj = {}; // 代理對象

Object.defineProperty(proxyObj, "name", {
  get() {
    console.log("訪問了 obj.name");
    return obj.name;
  },
  set(val) {
    console.log("修改了 obj.name");
    obj.name = val;
  },
});

現(xiàn)在我們只要訪問或修改代理對象的 name 屬性知市,就可以實(shí)現(xiàn)訪問或修改obj.name傻盟,同時(shí)打印信息到控制臺(tái)

Vue2 就是通過此方法將 data 中的屬性添加到 vm 實(shí)例上,因此我們可以使用this.屬性名來訪問屬性初狰,并且和我們打印信息到控制臺(tái)一樣莫杈,Vue 也添加了額外的操作比如通過 set 實(shí)現(xiàn)數(shù)據(jù)監(jiān)聽,從而完成響應(yīng)式變化

小結(jié)

  1. 根據(jù)單一職責(zé)原則:就一個(gè)類(通常也包括對象和函數(shù))而言奢入,應(yīng)該只有一個(gè)職責(zé)筝闹。
  2. 我們利用代理模式讓代理對象承擔(dān)額外功能,不破壞目標(biāo)對象腥光,從而不至于讓目標(biāo)對象變得臃腫而降低復(fù)用性和可維護(hù)性

3 JavaScript Proxy

JS 提供了 Proxy 類关顷,可以非常方便地創(chuàng)建代理對象,從而實(shí)現(xiàn)基本操作的攔截和自定義(如屬性查找武福、賦值议双、枚舉、函數(shù)調(diào)用等)捉片。

Proxy 的用法非常簡單:

const proxy = new Proxy(target, handler)
// target
// 要使用 Proxy 包裝的目標(biāo)對象(可以是任何類型的對象平痰,包括原生數(shù)組,函數(shù)伍纫,甚至另一個(gè)代理)宗雇。

// handler
// 一個(gè)通常以函數(shù)作為屬性的對象,各屬性中的函數(shù)分別定義了在執(zhí)行各種操作時(shí)代理 p 的行為莹规。

詳見 MDN 文檔 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy

3.1 Proxy 實(shí)現(xiàn)緩存代理

handler 對象有很多可選方法赔蒲,其中 apply 方法用來攔截函數(shù)調(diào)用操作

apply 方法接受 3 個(gè)參數(shù),詳見https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/apply

// apply 的 3 個(gè)參數(shù)
// target 目標(biāo)對象
// thisArg 被調(diào)用時(shí)的上下文對象
// argArray 被調(diào)用時(shí)的參數(shù)數(shù)組良漱。
const geoCache = {};
const getLatLngProxy = new Proxy(getLatLng, {
  apply(target, thisArg, argArray) {
    const address = argArray[0];
    console.log("緩存=" + geoCache[address]);
    return (geoCache[address] ??= target(address));
  },
});

getLatLngProxy("Hangzhou"); // 緩存=undefined
getLatLngProxy("Hangzhou"); // 緩存=杭州經(jīng)緯度

我們調(diào)用代理函數(shù) getLatLngProxy 時(shí)會(huì)觸發(fā) apply 方法

注意這里我們的目標(biāo)對象是 getLatLng 函數(shù)舞虱,即 apply 的 target 就是 getLatLng 的引用,因此我們調(diào)用 target 就相當(dāng)于調(diào)用 getLatLng

3.2 Vue3 的數(shù)據(jù)代理

Vue2 使用 Object.defineProperty 來實(shí)現(xiàn)數(shù)據(jù)代理母市,但是這個(gè)方法存在局限性矾兜,比如:普通屬性我們可以通過 set 方法獲取到其變化的信息,但是使用 push 方法改變數(shù)組患久,無法通過 set 獲取到焕刮。

因此 Vue3 改用 Proxy 來實(shí)現(xiàn)數(shù)據(jù)代理

和 apply 方法類似,handler 中還有 get 和 set 方法用來攔截對屬性訪問墙杯、修改的操作

詳見

const obj = {
  name: "JiMing",
};

const proxyObj = new Proxy(obj, {
  // target 目標(biāo)對象 即 obj
  // property 被獲取的屬性名配并。
  get(target, property) {
    console.log(`訪問了 obj.${property}`);
    return target[property];
  },
  // target 目標(biāo)對象 即 obj
  // 將被設(shè)置的屬性名
  set(target, property, value) {
    console.log(`修改了 obj.${property}`);
    target[property] = value;
  },
});

proxyObj.name; // 訪問了 obj.name
proxyObj.name = "Ji"; // 修改了 obj.name

完結(jié),撒花??

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末高镐,一起剝皮案震驚了整個(gè)濱河市溉旋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌嫉髓,老刑警劉巖观腊,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件邑闲,死亡現(xiàn)場離奇詭異,居然都是意外死亡梧油,警方通過查閱死者的電腦和手機(jī)苫耸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來儡陨,“玉大人褪子,你說我怎么就攤上這事∑澹” “怎么了嫌褪?”我有些...
    開封第一講書人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長胚股。 經(jīng)常有香客問我笼痛,道長,這世上最難降的妖魔是什么琅拌? 我笑而不...
    開封第一講書人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任缨伊,我火速辦了婚禮,結(jié)果婚禮上进宝,老公的妹妹穿的比我還像新娘倘核。我一直安慰自己,他們只是感情好即彪,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著活尊,像睡著了一般隶校。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蛹锰,一...
    開封第一講書人閱讀 52,158評(píng)論 1 308
  • 那天深胳,我揣著相機(jī)與錄音,去河邊找鬼铜犬。 笑死舞终,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的癣猾。 我是一名探鬼主播敛劝,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼纷宇!你這毒婦竟也來了夸盟?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤像捶,失蹤者是張志新(化名)和其女友劉穎上陕,沒想到半個(gè)月后桩砰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡释簿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年亚隅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片庶溶。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡煮纵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出渐尿,到底是詐尸還是另有隱情醉途,我是刑警寧澤,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布砖茸,位于F島的核電站隘擎,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏凉夯。R本人自食惡果不足惜货葬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望劲够。 院中可真熱鬧震桶,春花似錦、人聲如沸征绎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽人柿。三九已至柴墩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間凫岖,已是汗流浹背江咳。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哥放,地道東北人歼指。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像甥雕,于是被迫代替她去往敵國和親踩身。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容