摘要:缺點不符合開閉原則,如果要改東西很麻煩,繼承重寫都不合適。預防低水平人員帶來的風險。開閉原則,高拓展性。這里的訂閱者稱為觀察者,而被觀察者稱為發布者,當一個事件發生,發布者會發布通知所有訂閱者,并常常以事件對象形式傳遞消息。
介紹
最近開始給自己每周訂個學習任務,學習結果反饋為一篇文章的輸出,做好學習記錄。
這一周(02.25-03.03)我定的目標是《JavaScript 模式》的第七章學習一遍,學習結果的反饋就是本篇文章啦。
由于內容實在太長,我將本文分為兩部分:
《JavaScript 模式》知識點整理(上)
《JavaScript 模式》知識點整理(下)
本文內容中主要參考《JavaScript 模式》,其中也有些案例是來自網上資料,有備注出處啦,如造成不便,請聯系我刪改。
過兩天我會把這篇文章收錄到我整理的知識庫 【Cute-JavaScript】 中,并已經同步到 【github】上面。
六.外觀模式(Facade Pattern) 1.概念介紹外觀模式(Facade Pattern)是一種簡單又常見的模式,它為一些復雜的子系統接口提供一個更高級的統一接口,方便對這些子系統的接口訪問。
它不僅簡化類中的接口,還對接口和調用者進行解耦,外觀模式也常被認為是開發者必備,它可以將一些復雜操作封裝起來,并創建一個簡單接口用于調用。
2.優缺點和應用場景 2.1優點輕量級,減少系統相互依賴。
提高靈活性。
提高了安全性。
2.2缺點不符合開閉原則,如果要改東西很麻煩,繼承重寫都不合適。
2.3應用場景為復雜的模塊或子系統提供外界訪問的模塊。
子系統相對獨立。
預防低水平人員帶來的風險。
項目重構。
3.基本案例經常我們在處理一些特殊情況的時候,需要一起調用好幾個方法,我們使用外觀模式,就可以將多個方法包裝成一個方法,哪里需要使用直接調用這個包裝好的方法就可以。
比如我們經常處理瀏覽器事件,需要同時調用stopPropagation()和preventDefault(),于是我們就可以新建一個外觀方法,實現這兩個方法同時調用:
let myEvent = { // ... stop: e => { e.stopPropagation(); e.preventDefault(); } };
然后我們也可以使用外觀模式,來做IE事件的兼容性:
let myEvent = { // ... stop: e => { // 其他 if(typeof e.preventDefault === "function"){ e.preventDefault(); } if(typeof e.stopPropagation === "function"){ e.stopPropagation(); } // IE if(typeof e.returnValue === "boolean"){ e.returnValue = false; } if(typeof e.cancelBubble === "boolean"){ e.cancelBubble = true; } } };七、代理模式(Proxy Pattern) 1.概念介紹
代理模式(Proxy Pattern) 為其他對象提供一種代理,來控制這個對象的訪問,代理是在客戶端和真實對象之間的介質。
簡單的理解:如我們需要請明星來做廣告,我們會先通過聯系Ta的經紀人,談好條件才會給明星簽合同。
2.優缺點和應用場景 2.1優點職責單一且清晰。
保護真實對象。
開閉原則,高拓展性。
2.2缺點由于在客戶端和真實對象間添加代理對象,導致請求處理速度變慢。
實現代理模式需要額外工作,有些代理模式實現起來非常復雜。
2.3應用場景需要隱藏或保護某個類,則為這個類添加代理。
需要給不同訪問者提供不同權限,則在代理類上做判斷。
需要為某個類添加功能,如添加日志緩存等,我們可以在代理的類做添加,而不管去改原來封裝好的類。
3.基本案例這里我們以吃午飯問題來學習代理模式。通常情況下,我們會有兩種方式解決午飯問題:“去餐廳吃”和“叫外賣”。
去餐廳吃的話,我們就是自己過去吃飯了唄,如果是叫外賣,我們就會通過外賣小哥來拿到午飯才能吃起來。
去餐廳吃(沒有使用代理模式)
// 定義午飯類 參數 菜名 let Lunch = function(greens){ this.greens = greens; } Lunch.prototype.getGreens = function(){ return this.greens; } // 定義我這個對象 let leo = { buy: function(greens){ console.log(`午飯吃${greens.getGreens()}`); } } // 去餐廳吃 leo.buy(new Lunch("青椒炒肉")); // 午飯吃青椒炒肉
叫外賣(有使用代理模式)
// 定義午飯類 參數 菜名 let Lunch = function(greens){ this.greens = greens; } Lunch.prototype.getGreens = function(){ return this.greens; } // 定義外賣小哥這個對象 let brother = { buy: function(lunch){ leo.buy(lunch.getGreens()); } } // 定義我這個對象 let leo = { buy: function(greens){ console.log(`午飯吃${greens}`); } } // 叫外賣 brother.buy(new Lunch("青椒炒肉")); // 午飯吃青椒炒肉
并且外賣小哥還會幫我們做一些其他事,比如幫我們帶瓶可樂,我們改造brother和leo這2個對象,再看看效果:
let brother = { buy: function(lunch){ if(leo.needCola) leo.buyCola(); leo.buy(lunch.getGreens()); } } let leo = { needCola: true, buy: function(greens){ console.log(`午飯吃${greens}`); }, buyCola: function(){ console.log(`順手買瓶可樂!`); } } brother.buy(new Lunch("青椒炒肉")); // 順手買瓶可樂! // 午飯吃青椒炒肉4.保護代理
還是借用 3.基本案例 的叫外賣的例子,我們現在要實現保護代理,而我們需要外賣小哥為了我們的身體健康,超過晚上9點,就不幫我們買可樂。
還是改造上面買可樂的brother對象代碼:
let brother = { buy: function(lunch){ let nowDate = new Date(); if(nowDate.getHours() >= 21){ console.log("親,這么晚不要喝可樂喲!"); }else{ if(leo.needCola) leo.buyCola(); leo.buy(lunch.getGreens()); } } } brother.buy(new Lunch("青椒炒肉")); // 順手買瓶可樂! // 午飯吃青椒炒肉5.虛擬代理
虛擬代理能把一些開銷大的對象,延遲到真正需要的時候才去創建和執行。
我們這里舉個圖片懶加載的例子:
這個案例參考自JS設計模式-代理模式.
// 圖片加載 let ele = (function(){ let node = document.createElement("img"); document.body.appendChild(node); return{ setSrc : function(src){ node.src = src; } } })() // 代理對象 let proxy = (function(){ let img = new Image(); img.onload = function(){ ele.setSrc(this.src); } return { setSrc : function(src){ img.src = src; ele.setSrc("loading.png"); } } })() proxy.setSrc("example.png");6.緩存代理
緩存代理是將一些開銷大的運算結果提供暫存功能,當下次計算時,參數和之前一直,則將緩存的結果返回:
這個案例參考自JS設計模式-代理模式.
//計算乘積 let mult = function(){ let result = 1; for(let i = 0; i八、中介者模式(Mediator Pattern) 1.概念介紹 中介者模式(Mediator Pattern) 是用來降低多個對象和類之間的通信復雜性,促進形成松耦合,提高可維護性。
在這種模式下,獨立的對象之間不能直接通信,而是需要中間對象(mediator對象),當其中一個對象(colleague對象)狀態改變后,它會通知mediator對象,
2.優缺點和應用場景 2.1優點
然后mediator對象會把該變換通知到任意需要知道此變化的colleague對象。降低類的復雜度,從一對多轉成一對一。
為各個類之間解耦。
提高代碼可維護性。
2.2缺點中介者會越來越龐大,變得難以維護。
2.3應用場景系統中對象之間存在比較復雜的引用關系,而且難以復用該對象。
需要生成最少的子類,實現一個中間類封裝多個類中的行為的時候。
另外: 不要在職責混亂的時候使用。
3.基本案例這里我們實現一個簡單的案例,一場測試結束后,公布結果,告知解答出題目的人挑戰成功,否則挑戰失敗:
這個案例來自JavaScript 中常見設計模式整理const player = function(name) { this.name = name; playerMiddle.add(name); } player.prototype.win = function() { playerMiddle.win(this.name); } player.prototype.lose = function() { playerMiddle.lose(this.name); } const playerMiddle = (function() { // 將就用下這個 demo,這個函數當成中介者 const players = []; const winArr = []; const loseArr = []; return { add: function(name) { players.push(name) }, win: function(name) { winArr.push(name) if (winArr.length + loseArr.length === players.length) { this.show() } }, lose: function(name) { loseArr.push(name) if (winArr.length + loseArr.length === players.length) { this.show() } }, show: function() { for (let winner of winArr) { console.log(winner + "挑戰成功;") } for (let loser of loseArr) { console.log(loser + "挑戰失敗;") } }, } }()) const a = new player("A 選手"); const b = new player("B 選手"); const c = new player("C 選手"); a.win() b.win() c.lose() // A 選手挑戰成功; // B 選手挑戰成功; // C 選手挑戰失敗;4.書本案例這個案例來自 《JavaScript 模式》第七章 中介者模式 的案例。
這里我們有這么一個游戲例子,規則是兩個玩家在規定時間內,比比誰點擊按鈕次數更多,玩家1按按鍵2,玩家2按按鍵0,并且計分板實時更新。這里的中介者需要知道所有其他對象信息,并且它需要知道哪個玩家點擊了一次,隨后通知玩家。玩家進行游戲的時候,還要通知中介者它做的事情,中介者更新分數并顯示比分。
這里的player對象都是通過Player()構造函數生成,并且都有points和name屬性,每次調用play()都會增加1分并通知中介者。
function Player(name){ this.points = 0; this.name = name; } Player.prototype.play = function(){ this.points += 1; mediator.played(); }計分板有個update()方法,當玩家回合結束就會調用,它不知道任何玩家的信息也沒有保存分值,只是實現展示當前分數。
let scoreboard = { // 待更新HTML元素 ele: document.getElementById("result"); // 更新比分 update: function (score){ let msg = ""; for(let k in score){ if(score.hasOwnProperty(k)){ msg = `${k} : ${score[k]}
` } } this.ele.innerHTML = msg; } }接下來創建mediator對象:
let mediator = { players: {}, // 所有玩家 setup: function(){ // 初始化 let players = this.players; players.homw = new Player("Home"); players.guest = new Player("Guest"); }, // 當有人玩時 更新分數 played: function(){ let players = this.players let score = { Home: players.home.points, Guest: players.guest.points, } scoreboard.update(score); } // 處理用戶交互 keypress: function(e){ e = e || window.event; // 兼容IE if(e.which === 49){ // 按鍵1 mediator.players.home.play(); } if(e.which === 48){ // 按鍵0 mediator.players.guest.play(); } } }最后就是需要運行和卸載游戲了:
mediator.setup(); window.onkeypress = mediator.keypress; // 游戲30秒后結束 setTimeout(function(){ window.onkeypress = null; alert("游戲結束"); }, 30000)九、觀察者模式(Observer Patterns) 1.概念介紹觀察者模式(Observer Patterns) 也稱訂閱/發布(subscriber/publisher)模式,這種模式下,一個對象訂閱定一個對象的特定活動,并在狀態改變后獲得通知。
這里的訂閱者稱為觀察者,而被觀察者稱為發布者,當一個事件發生,發布者會發布通知所有訂閱者,并常常以事件對象形式傳遞消息。所有瀏覽器事件(鼠標懸停,按鍵等事件)都是該模式的例子。
我們還可以這么理解:這就跟我們訂閱微信公眾號一樣,當公眾號(發布者)群發一條圖文消息給所有粉絲(觀察者),然后所有粉絲都會接受到這篇圖文消息(事件),這篇圖文消息的內容是發布者自定義的(自定義事件),粉絲閱讀后可能就會買買買(執行事件)。
2.觀察者模式 VS 發布訂閱模式 2.1觀察者模式一種一對多的依賴關系,多個觀察者對象同時監聽一個主題對象。這個主題對象在狀態上發生變化時,會通知所有觀察者對象,使它們能夠自動更新自己。
2.2發布訂閱模式發布訂閱模式理念和觀察者模式相同,但是處理方式上不同。
2.3兩者異同點
在發布訂閱模式中,發布者和訂閱者不知道對方的存在,他們通過調度中心串聯起來。
訂閱者把自己想訂閱的事件注冊到調度中心,當該事件觸發時候,發布者發布該事件到調度中心(并攜帶上下文),由調度中心統一調度訂閱者注冊到調度中心的處理代碼。觀察者模式中,觀察者知道發布者是誰,并發布者保持對觀察者進行記錄。而發布訂閱模式中,發布者和訂閱者不知道對方的存在。它們只是通過調度中心進行通信。
發布訂閱模式中,組件是松散耦合的,正好和觀察者模式相反。
觀察者模式大多是同步,如當事件觸發,發布者就會去調用觀察者的方法。而發布訂閱模式大多是異步的(使用消息隊列)。
觀察者模式需要在單個應用程序地址空間中實現,而發布-訂閱更像交叉應用模式。
盡管存在差異,但也有人說發布-訂閱模式是觀察者模式的變異,因為它們概念上相似。
2.4兩者優缺點相同優點:
都可以一對多
程序便于擴展
不同優點:
觀察者模式:單向解耦,發布者不需要清楚訂閱者何時何地訂閱,只需要維護訂閱隊列,發送消息即可
發布訂閱模式:雙向解耦,發布者和訂閱者都不用清楚對方,全部由訂閱中心做處理
缺點:
如果一個被觀察者和多個觀察者的話,會增加維護的難度,并且會消耗很多時間。
如果觀察者和發布者之間有循環依賴,可能會導致循環調用引起系統奔潰。
觀察者無法得知觀察的目標對象是如何發生變化,只能知道目標對象發生了變化。
發布訂閱模式,中心任務過重,一旦崩潰,所有訂閱者都會受到影響。
4.基本案例我們平常一直使用的給DOM節點綁定事件,也是觀察者模式的案例:
document.body.addEventListener("click", function(){ alert("ok"); },false); document.body.click();這里我們訂閱了document.body的click事件,當body點擊它就向訂閱者發送消息,就會彈框ok。我們也可以添加很多的訂閱。
4.觀察者模式 案例本案例來自 javascript 觀察者模式和發布訂閱模式。
class Dom { constructor() { // 訂閱事件的觀察者 this.events = {} } /** * 添加事件的觀察者 * @param {String} event 訂閱的事件 * @param {Function} callback 回調函數(觀察者) */ addEventListener(event, callback) { if (!this.events[event]) { this.events[event] = [] } this.events[event].push(callback) } removeEventListener(event, callback) { if (!this.events[event]) { return } const callbackList = this.events[event] const index = callbackList.indexOf(callback) if (index > -1) { callbackList.splice(index, 1) } } /** * 觸發事件 * @param {String} event */ fireEvent(event) { if (!this.events[event]) { return } this.events[event].forEach(callback => { callback() }) } } const handler = () => { console.log("fire click") } const dom = new Dom() dom.addEventListener("click", handler) dom.addEventListener("move", function() { console.log("fire click2") }) dom.fireEvent("click")5.發布訂閱模式 案例本案例來自 javascript 觀察者模式和發布訂閱模式。
class EventChannel { constructor() { // 主題 this.subjects = {} } hasSubject(subject) { return this.subjects[subject] ? true : false } /** * 訂閱的主題 * @param {String} subject 主題 * @param {Function} callback 訂閱者 */ on(subject, callback) { if (!this.hasSubject(subject)) { this.subjects[subject] = [] } this.subjects[subject].push(callback) } /** * 取消訂閱 */ off(subject, callback) { if (!this.hasSubject(subject)) { return } const callbackList = this.subjects[subject] const index = callbackList.indexOf(callback) if (index > -1) { callbackList.splice(index, 1) } } /** * 發布主題 * @param {String} subject 主題 * @param {Argument} data 參數 */ emit(subject, ...data) { if (!this.hasSubject(subject)) { return } this.subjects[subject].forEach(callback => { callback(...data) }) } } const channel = new EventChannel() channel.on("update", function(data) { console.log(`update value: ${data}`) }) channel.emit("update", 123)參考資料《JavaScript Patterns》
Author 王平安 pingan8787@qq.com 博 客 www.pingan8787.com 微 信 pingan8787 每日文章推薦 https://github.com/pingan8787... JS小冊 js.pingan8787.com 微信公眾號 前端自習課
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/102338.html
摘要:單體模式有以下優點用來劃分命名空間,減少全局變量數量。通常我們使用操作符創建單體模式的三種選擇,讓構造函數總返回最初的對象使用全局對象來存儲該實例不推薦,容易全局污染。實現該工廠模式并不困難,主要是要找到能夠穿件所需類型對象的構造函數。 介紹 最近開始給自己每周訂個學習任務,學習結果反饋為一篇文章的輸出,做好學習記錄。 這一周(02.25-03.03)我定的目標是《JavaScri...
摘要:介紹這周開始學習老姚大佬的正則表達式迷你書,然后習慣性的看完一遍后,整理一下知識點,便于以后自己重新復習。感謝原書作者老姚,本文無意抄襲,只是作為自己知識點的整理,后續也會整理到自己的知識庫網站中。等價于,表示出現次。 showImg(https://segmentfault.com/img/remote/1460000018530584?w=919&h=449); 介紹 這周開始學習...
摘要:部分如上,四個區塊,四大名著,嘎嘎代碼如上,寫的很爛,輕噴用來控制元素的顯示和隱藏,實際上是為了實現動畫效果此處有裝逼的嫌疑,因為會阻礙,而不會,另外也可以用來代替。 說明 又是一個練手的小玩意兒,本身沒什么技術含量,就是幾個不常用的CSS3特性的結合而已。 要點 Label標簽的for屬性 單選框的:checked偽類 CSS的加號[+]選擇器 效果圖 showImg(https...
摘要:部分如上,四個區塊,四大名著,嘎嘎代碼如上,寫的很爛,輕噴用來控制元素的顯示和隱藏,實際上是為了實現動畫效果此處有裝逼的嫌疑,因為會阻礙,而不會,另外也可以用來代替。 說明 又是一個練手的小玩意兒,本身沒什么技術含量,就是幾個不常用的CSS3特性的結合而已。 要點 Label標簽的for屬性 單選框的:checked偽類 CSS的加號[+]選擇器 效果圖 showImg(https...
摘要:模塊化是隨著前端技術的發展,前端代碼爆炸式增長后,工程化所采取的必然措施。目前模塊化的思想分為和。特別指出,事件不等同于異步,回調也不等同于異步。將會討論安全的類型檢測惰性載入函數凍結對象定時器等話題。 Vue.js 前后端同構方案之準備篇——代碼優化 目前 Vue.js 的火爆不亞于當初的 React,本人對寫代碼有潔癖,代碼也是藝術。此篇是準備篇,工欲善其事,必先利其器。我們先在代...
閱讀 2632·2021-10-14 09:47
閱讀 4909·2021-09-22 15:52
閱讀 3355·2019-08-30 15:53
閱讀 1428·2019-08-30 15:44
閱讀 669·2019-08-29 16:41
閱讀 1646·2019-08-29 16:28
閱讀 439·2019-08-29 15:23
閱讀 1618·2019-08-26 12:20