<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
相信大家在日常學習和工作中都多多少少聽說/瞭解/使用過 設計模式,我們都知道,使用恰當的設計模式可以優化我們的程式碼,那你是否知道對於前端開發哪些 設計模式 是日常工作經常用到或者必須掌握的呢?本文我將帶大家一起學習下前端常見的設計模式以及它們的 使用場景!!!
本文主講:
適合人群:
策略模式的定義是:定義一系列的演演算法,把它們一個個封裝起來,並且使它們可以相互替換。從定義不難看出,策略模式是用來解決那些一個功能有多種方案、根據不同條件輸出不同結果且條件很多的場景,而這些場景在我們工作中也經常遇到,接下來我將用幾個例子來展示策略模式在哪裡用以及如何用。
假如我們有這麼一個需求,需要根據員工的績效考核給員工發放年終獎(分為A/B/C/D四個等級 分別對應基礎獎金的1/2/3/4倍),我們很容易就寫出這樣的程式碼
//level 評級 basicBonus 基礎獎金 const computeBonus(level, basicBonus) = () => { if(level === 'A') { return basicBonus * 1; } else if(level === 'B') { return basicBonus * 2; } else if(level === 'C') { return basicBonus * 3; } else if(level === 'D') { return basicBonus * 4; } } computeBonus('A', 1000);//1000
我們發現,以上的程式碼可以輕鬆實現我們的需求,但是這些程式碼存在什麼問題呢?
computedBonus
方法十分臃腫,包含太多if-else
那策略模式是怎麼解決這些問題的呢?我們都知道,設計模式的核心之一就是將可變的和不可變的部分抽離分裝,那我們根據這個原則來修改我們的程式碼,其中可變的就是如何使用這些演演算法(多少個評級),不變的是演演算法的內容(評級對應的獎金),下面就是改變後的程式碼
//定義策略類 const strategies = { 'A': function(basicBonus) { return basicBonus * 1; }, 'B': function(basicBonus) { return basicBonus * 2; }, 'C': function(basicBonus) { return basicBonus * 3; }, 'D': function(basicBonus) { return basicBonus * 4; }, } //使用策略類 const computeBonus = (level, basicBonus) { return strategies[level](basicBonus); } computeBouns('A', 1000);//1000
從上面可以看出,我們將每種情況都單獨弄成一個策略,然後根據傳入評級和獎金計算年終獎,這樣我們的computeBonus
方法程式碼量大大減少,也不用冗雜的if-else
分支,同時,如果我們想要改變規則,只需要在strategies
中新增對應的策略,增加了程式碼的健壯性
我們日常的工作中,不可避免地需要做表單相關的業務,畢竟這是前端最初始的職能之一。而表單繞不開表單驗證,那接下來我將帶大家看看策略模式在表單中如何使用。
需求: 假設我們有一個登入業務,提交表單前需要做驗證,驗證規則如下:1.使用者名稱稱不能為空,2.密碼不能少於6位,3.手機格式要正確。
我們很容易寫出以下程式碼
const verifyForm = (formData) => { if(formData.userName == '') { console.log('使用者名稱不能為空'); return false }; if(formData.password.length < 6) { console.log('密碼長度不能小於6位'); return false; } if(( !/(^1[3|5|8][0-9]{9}$)/.test(formData.phone)) { console.log('手機格式錯誤'); return false } }
顯然,這樣也可以完成表單校驗的功能,但是這樣寫同樣存在著上面說的問題,接下來,我們看下用策略模式如何改寫
//編寫策略物件 const strategies = { isEmpty: function(value, error) { if(value === '' { return error; }) }, minLength: function(value, len, error) { if(value.length < len { return error; }) }, isPhone: function(value, error) { if ( !/(^1[3|5|8][0-9]{9}$)/.test( value ) ){ return errorMsg; } }; } //接下來我們編寫實現類 用於生成對應的策略範例 class Validator { controustor(cache) { this.cache = cache || []; //儲存校驗規則 }; add(dom, rule, error) { const arr = rule.splt(':');//分離引數 this.cache.push(function(){ // 把校驗的步驟用空函數包裝起來,並且放入 cache const strategy = arr.shift(); // 使用者挑選的 strategy arr.unshift( dom.value ); // 把 input 的 value 新增進參數列 arr.push( errorMsg ); // 把 error 新增進參數列 return strategies[ strategy ].apply( dom, ary ); }); }; start() { for ( let i = 0, validatorFunc; validatorFunc = this.cache[ i++ ]; ){ var msg = validatorFunc(); // 開始校驗,並取得校驗後的返回資訊 if ( msg ){ // 如果有確切的返回值,說明校驗沒有通過 return msg; } } } } //編寫完策略物件和範例類後我們就可以看看如何使用了 const validataFunc = function(){ let validator = new Validator(); // 建立一個 validator 物件 /***************新增一些校驗規則****************/ validator.add( registerForm.userName, 'isNonEmpty', '使用者名稱不能為空' ); validator.add( registerForm.password, 'minLength:6', '密碼長度不能少於 6 位' ); validator.add( registerForm.phoneNumber, 'isMobile', '手機號碼格式不正確' ); var errorMsg = validator.start(); // 獲得校驗結果 return errorMsg; // 返回校驗結果 } var registerForm = document.getElementById( 'registerForm' ); registerForm.onsubmit = function(){ var errorMsg = validataFunc(); // 如果 errorMsg 有確切的返回值,說明未通過校驗 if ( errorMsg ){ alert ( errorMsg ); return false; // 阻止表單提交 } };
這樣,我們就用策略模式將需求改好了,之後如果我們的校驗規則改變了,修改起來也是很方便的,比如:
validator.add( registerForm.userName, 'isNonEmpty', '使用者名稱不能為空' ); // 改成:
validator.add( registerForm.userName, 'minLength:10', '使用者名稱長度不能小於 10 位' );
而且,我們也可以給文字方塊新增多個校驗規則,只需要修改下策略物件以及策略方法即可!大大地增強了程式碼地健壯性。
優點:
if-else
)缺點:
代理模式是為一個物件提供一個代用品或預留位置,以便控制對它的存取。代理模式應該是我們日常用到比較多的設計模式了(我們日常工作中不知不覺就會用到代理模式,可能你沒發現而已)。
代理模式分為保護代理(用於控制不同許可權的物件對目標物件的存取)和虛擬代理(把開銷很大的物件或者操作延遲到真正需要的時候再去建立 類比引入時動態引入)兩種,但是前端基本不用到保護代理,或者說很難實現保護代理,所以大部分情況下我們用的都是虛擬代理,接下來我主要也是講虛擬代理!
舉個例子,加入A想要給C送情書,但是A沒有直接把情書交給C,而是讓B代為傳送情書,那麼B就是代理,他的職責就是替A做事,這個就是最簡單的代理模式,接下來我們還是老樣子,邊寫需求邊講解
相信大家對於圖片懶載入都不陌生吧,他可以在我們載入出目標圖片前預載入佔點陣圖片,避免空白區域影響體驗,那我們很容易就能寫出下面的程式碼
const lazyImage = (function() { let imgNode = document.createElement('img'); document.body.appendChild(imgNode); let image = new Image; image.onload = function() { imgNode.src = image.src;//在這裡設定圖片的真正路由 }; return { setSrc: function(src) { imgNode.src = '....'//預載入本地圖片; image.src = src } } })() lazyImage.setSrc('https://s3.ap-northeast-1.wasabisys.com/img.it145.com/202208/olddogyjzswslrw5e.jpg');//載入真正的圖片
我們看上面的程式碼,也可以完成預載入的功能,但是這樣的程式碼存在著什麼樣的問題呢
接下來,我們就用代理模式來改寫一下這個例子
const lazyImage = (function() { let imageNode = document.createElement('img'); document.body.appendChild(imageNode); return { setSrc: function(src) { imageNode.src = src;//設定目標src } } })() //代理函數 const proxyImage = (function() { let image = new Image; image.onload = function() { myImage.setSrc(this.src); } return { setSrc: function(src) { myImage.setSrc('....')//預載入本地圖片 img.src = src } } })() proxyImage.setSrc('https://s3.ap-northeast-1.wasabisys.com/img.it145.com/202208/olddogyjzswslrw5e.jpg');//使用代理載入
我們觀察用代理模式寫的程式碼,發現我們將預載入的邏輯轉移到了代理函數中,這樣有啥好處呢
proxyImage.setSrc(...)
改成lazyImage.setSrc(...)
不知道大家有沒有發現,代理函數和原函數有一部分相似的邏輯和操作,只是代理函數的功能更多,這其實也是代理模式的特徵之一,代理函數在保證實現原函數的基本功能的前提下實現更多功能,這樣即使使用者不清楚邏輯也能直接使用,而且後期改動成本很低,只需要改回原函數的使用即可!!
設想一下,如果現在要你寫一個簡單的求積函數,你會怎麼寫
const mult = function() { let result = 1; for(let i = 0, len = arguments.length; i < len; i++) { result *= arguments[i]; } return result } mult(1, 2, 3);//6
我們來看一下上面的程式碼有啥缺點,上面的程式碼雖然實現了求積,但是如果我們mult(1,2,3)
之後再去mult(1,2,3)
,那麼系統還是會再計算一遍,這樣無疑是效能浪費,那麼我們就能用代理模式來改寫:
const proxyMult = (function() { let cache = {};//快取計算結果 return function() { const args = Array.prototype.join.call( arguments, ','); if(args in cache) { return cache[args] } return cache[args] = mult.apply(this.arguments) } })(); proxyMult(1,2,3);//6 proxyMult(1, 2, 3);//輸出6 但是不會重新計算
可以看到,我們用代理模式改寫後避免了重複運算的浪費,這只是一種情景,還有其他相似情景,比如我們分頁請求資料,可以使用相似的思路,避免對同頁的資料重複請求,這在工作中非常有用!!
我們日常工作中還有很多地方用到代理,比如代理合並請求(間斷性合併而不是全部合併,減少伺服器壓力)、惰性載入或建立申請資源等等,而什麼時候使用代理其實不需要提前花很多精力去思考,當我們寫著寫著發現可以抽離使用代理模式的時候再去使用也不遲。由於文章篇幅有限,本文就先講解策略模式和代理模式,後續將繼續更新其他實用的設計模式,喜歡的小夥伴可以點個贊和關注一下,有啥問題可以評論區一起學習交流!
以上就是JS前端中的設計模式和使用場景範例詳解的詳細內容,更多關於前端設計模式場景的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45