本篇內(nèi)容主要講解“JavaScript5大常用設(shè)計(jì)模式是什么”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“JavaScript5大常用設(shè)計(jì)模式是什么”吧!
成都創(chuàng)新互聯(lián)不只是一家網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司;我們對(duì)營(yíng)銷、技術(shù)、服務(wù)都有自己獨(dú)特見解,公司采取“創(chuàng)意+綜合+營(yíng)銷”一體化的方式為您提供更專業(yè)的服務(wù)!我們經(jīng)歷的每一步也許不一定是最完美的,但每一步都有值得深思的意義。我們珍視每一份信任,關(guān)注我們的成都做網(wǎng)站、成都網(wǎng)站建設(shè)質(zhì)量和服務(wù)品質(zhì),在得到用戶滿意的同時(shí),也能得到同行業(yè)的專業(yè)認(rèn)可,能夠?yàn)樾袠I(yè)創(chuàng)新發(fā)展助力。未來(lái)將繼續(xù)專注于技術(shù)創(chuàng)新,服務(wù)升級(jí),滿足企業(yè)一站式營(yíng)銷型網(wǎng)站建設(shè)需求,讓再小的成都品牌網(wǎng)站建設(shè)也能產(chǎn)生價(jià)值!
設(shè)計(jì)模式的定義
設(shè)計(jì)模式是在面向?qū)ο筌浖O(shè)計(jì)過(guò)程中針對(duì)特定問(wèn)題的簡(jiǎn)潔而優(yōu)雅的解決方案。在不同的編程語(yǔ)言中,對(duì)設(shè)計(jì)模式的實(shí)現(xiàn)其實(shí)是可能會(huì)有區(qū)別的。比如java和javascript,在Java這種靜態(tài)編譯型語(yǔ)言中,無(wú)法動(dòng)態(tài)地給已存在的對(duì)象添加職責(zé),所以一般通過(guò)包裝類的方式來(lái)實(shí)現(xiàn)裝飾者模式。但在JavaScript這種動(dòng)態(tài)解釋型語(yǔ)言中,給對(duì)象動(dòng)態(tài)添加職責(zé)是再簡(jiǎn)單不過(guò)的事情。這就造成了JavaScript語(yǔ)言的裝飾者模式不再關(guān)注于給對(duì)象動(dòng)態(tài)添加職責(zé),而是關(guān)注于給函數(shù)動(dòng)態(tài)添加職責(zé)。
一、工廠模式
工廠模式是用來(lái)創(chuàng)建對(duì)象的一種最常用的設(shè)計(jì)模式,不暴露創(chuàng)建對(duì)象的具體邏輯,而是將將邏輯封裝在一個(gè)函數(shù)中,那么這個(gè)函數(shù)就可以被視為一個(gè)工廠,工廠模式根據(jù)抽象程度的不同可以分為:簡(jiǎn)單工廠,工廠方法和抽象工廠,接下來(lái),將對(duì)簡(jiǎn)單工廠和工廠方法在JavaScript中的運(yùn)用舉個(gè)簡(jiǎn)單的例子:
1. 簡(jiǎn)單工廠
簡(jiǎn)單工廠模式又叫靜態(tài)工廠模式,由一個(gè)工廠對(duì)象決定創(chuàng)建某一種產(chǎn)品對(duì)象類的實(shí)例,主要用來(lái)創(chuàng)建同一類對(duì)象
比如說(shuō),在實(shí)際的項(xiàng)目中,我們常常需要根據(jù)用戶的權(quán)限來(lái)渲染不同的頁(yè)面,高級(jí)權(quán)限的用戶所擁有的頁(yè)面有些是無(wú)法被低級(jí)權(quán)限的用戶所查看,所以我們可以在不同權(quán)限等級(jí)用戶的構(gòu)造函數(shù)中,保存該用戶能夠看到的頁(yè)面。
let UserFactory = function (role) { function SuperAdmin() { this.name = "超級(jí)管理員", this.viewPage = ['首頁(yè)', '用戶管理', '訂單管理', '應(yīng)用管理', '權(quán)限管理'] } function Admin() { this.name = "管理員", this.viewPage = ['首頁(yè)', '訂單管理', '應(yīng)用管理'] } function NormalUser() { this.name = '普通用戶', this.viewPage = ['首頁(yè)', '訂單管理'] } switch (role) { case 'superAdmin': return new SuperAdmin(); break; case 'admin': return new Admin(); break; case 'user': return new NormalUser(); break; default: throw new Error('參數(shù)錯(cuò)誤, 可選參數(shù):superAdmin、admin、user'); } } //調(diào)用 let superAdmin = UserFactory('superAdmin'); let admin = UserFactory('admin') let normalUser = UserFactory('user')
總結(jié):在上面的例子中,UserFactory就是一個(gè)簡(jiǎn)單工廠,在該函數(shù)中有3個(gè)構(gòu)造函數(shù)分別對(duì)應(yīng)不同的權(quán)限的用戶,當(dāng)我們調(diào)用工廠函數(shù)時(shí),只需要傳遞superAdmin, admin, user這三個(gè)可選參數(shù)中的一個(gè)獲取對(duì)應(yīng)的實(shí)例對(duì)象
優(yōu)點(diǎn):簡(jiǎn)單工廠的優(yōu)點(diǎn)在于,你只需要一個(gè)正確的參數(shù),就可以獲取到你所需要的對(duì)象,而無(wú)需知道其創(chuàng)建的具體細(xì)節(jié);
缺點(diǎn):在函數(shù)內(nèi)包含了所有對(duì)象的創(chuàng)建邏輯(構(gòu)造函數(shù))和判斷邏輯的代碼,每增加新的構(gòu)造函數(shù)還需要修改判斷邏輯代碼,我們的對(duì)象不是上面的3個(gè)而是30個(gè)或更多時(shí),這個(gè)函數(shù)會(huì)成為一個(gè)龐大的超級(jí)函數(shù),便得難以維護(hù),簡(jiǎn)單工廠只能作用于創(chuàng)建的對(duì)象數(shù)量較少,對(duì)象的創(chuàng)建邏輯不復(fù)雜時(shí)使用;
2. 工廠方法
工廠方法模式的本意是將實(shí)際創(chuàng)建對(duì)象的工作推遲到子類中,這樣核心類就變成了抽象類,但是在JavaScript中很難像傳統(tǒng)面向?qū)ο竽菢尤?shí)現(xiàn)創(chuàng)建抽象類,所以在JavaScript中我們只需要參考它的核心思想即可,我們可以將工廠方法看作是一個(gè)實(shí)例化對(duì)象的工廠類
比如說(shuō)上面的例子,我們用工廠方法可以這樣寫,工廠方法我們只把它看作是一個(gè)實(shí)例化對(duì)象的工廠,它只做實(shí)例化對(duì)象這一件事情,我們采用安全模式創(chuàng)建對(duì)象
//安全模式創(chuàng)建的工廠方法函數(shù) let UserFactory = function(role) { if(this instanceof UserFactory) { var s = new this[role](); return s; } else { return new UserFactory(role); } } //工廠方法函數(shù)的原型中設(shè)置所有對(duì)象的構(gòu)造函數(shù) UserFactory.prototype = { SuperAdmin: function() { this.name = "超級(jí)管理員", this.viewPage = ['首頁(yè)', '用戶管理', '訂單管理', '應(yīng)用管理', '權(quán)限管理'] }, Admin: function() { this.name = "管理員", this.viewPage = ['首頁(yè)', '訂單管理', '應(yīng)用管理'] }, NormalUser: function() { this.name = '普通用戶', this.viewPage = ['首頁(yè)', '訂單管理'] } } //調(diào)用 let superAdmin = UserFactory('SuperAdmin'); let admin = UserFactory('Admin') let normalUser = UserFactory('NormalUser')
總結(jié):在簡(jiǎn)單工廠中,如果我們新增加一個(gè)用戶類型,需要修改兩個(gè)地方的代碼,一個(gè)是增加新的用戶構(gòu)造函數(shù),一個(gè)是在邏輯判斷中增加對(duì)新的用戶的判斷,而在抽象工廠方法中,我們只需要在UserFactory.prototype中添加就可以啦。
二、單例模式
定義:是保證一個(gè)類只有一個(gè)實(shí)例,并且提供一個(gè)訪問(wèn)它的全局訪問(wèn)點(diǎn)。
需求:一些對(duì)象我們往往只需要一個(gè),比如線程池、全局緩存、瀏覽器中的window對(duì)象、登錄浮窗等。
實(shí)現(xiàn):用一個(gè)變量標(biāo)識(shí)當(dāng)前是否已經(jīng)為某個(gè)類創(chuàng)建過(guò)對(duì)象,如果是,則在下一次獲取這個(gè)類的實(shí)例時(shí),直接返回之前創(chuàng)建的對(duì)象。
優(yōu)點(diǎn):
可以用來(lái)劃分命名空間,減少全局變量的數(shù)量
可以被實(shí)例化,且實(shí)例化一次,再次實(shí)例化生成的也是***個(gè)實(shí)例
下面舉個(gè)例子,在js中,我們可以使用閉包來(lái)創(chuàng)建實(shí)現(xiàn)這種模式:
var single = (function(){ var unique; function getInstance(){ // 如果該實(shí)例存在,則直接返回,否則就對(duì)其實(shí)例化 if( unique === undefined ){ unique = new Construct(); } return unique; } function Construct(){ // ... 生成單例的構(gòu)造函數(shù)的代碼 } return { getInstance : getInstance } })();
總結(jié):在上面的代碼中,我們可以使用single.getInstance來(lái)獲取到單例,并且每次調(diào)用均獲取到同一個(gè)單例,在我們平時(shí)的開發(fā)中,我們也經(jīng)常會(huì)用到這種模式,比如當(dāng)我們單擊登錄按鈕的時(shí)候,頁(yè)面中會(huì)出現(xiàn)一個(gè)登錄框,而這個(gè)浮窗是唯一的,無(wú)論單擊多少次登錄按鈕,這個(gè)浮窗只會(huì)被創(chuàng)建一次,因此這個(gè)登錄浮窗就適合用單例模式。
三、代理模式
代理模式主要是為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn),主要解決在直接訪問(wèn)對(duì)象時(shí)帶來(lái)的問(wèn)題,比如說(shuō):要訪問(wèn)的對(duì)象在遠(yuǎn)程的機(jī)器上,在面向?qū)ο笙到y(tǒng)中,有些對(duì)象由于某些原因(比如對(duì)象創(chuàng)建開銷很大,或者某些操作需要安全控制,或者需要進(jìn)程外的訪問(wèn)),直接訪問(wèn)會(huì)給使用者或者系統(tǒng)結(jié)構(gòu)帶來(lái)很多麻煩,我們可以在訪問(wèn)此對(duì)象時(shí)加上一個(gè)對(duì)此對(duì)象的訪問(wèn)層。
代理模式最基本的形式是對(duì)訪問(wèn)進(jìn)行控制,代理對(duì)象和另一個(gè)對(duì)象(本體)實(shí)現(xiàn)的是同樣的接口,實(shí)際上工作還是本體在做,它才是負(fù)責(zé)執(zhí)行所分派的任務(wù)的那個(gè)對(duì)象或類,代理對(duì)象所做的不外乎節(jié)制對(duì)本體的訪問(wèn),代理對(duì)象并不會(huì)在另一對(duì)象的基礎(chǔ)上添加方法或修改其方法,也不會(huì)簡(jiǎn)化那個(gè)對(duì)象的接口,它實(shí)現(xiàn)的接口與本體完全相同,所有對(duì)它進(jìn)行的方法調(diào)用都會(huì)被傳遞給本體。
(function(){ // 示例代碼 // 目標(biāo)對(duì)象,是真正被代理的對(duì)象 function Subject(){} Subject.prototype.request = function(){}; /** * 代理對(duì)象 * @param {Object} realSubject [持有被代理的具體的目標(biāo)對(duì)象] */ function Proxy(realSubject){ this.realSubject = readSubject; } Proxy.prototype.request = function(){ this.realSubject.request(); }; }());
總結(jié):在上面的代碼中,Proxy可以控制對(duì)真正被代理對(duì)象的一個(gè)訪問(wèn),在代理模式中,比較常見的就是虛擬代理,虛擬代理用于控制對(duì)那種創(chuàng)建開銷很大的本體的訪問(wèn),它會(huì)把本體的實(shí)例化推遲到有方法被調(diào)用的時(shí)候,比如說(shuō),現(xiàn)在我們假設(shè)PublicLibrary的實(shí)例化很慢,不能在網(wǎng)頁(yè)加載的時(shí)候立即完成,我們可以為其創(chuàng)建一個(gè)虛擬代理,讓它把PublicLibrary的實(shí)例化推遲到必要的時(shí)候,比如說(shuō)我們?cè)谇岸酥薪?jīng)常用到的圖片懶加載,就可以用虛擬代理;
四、觀察者模式
如果大家學(xué)過(guò)一些像vue,react這些框架,相信大家對(duì)觀察者模式一定很熟悉,現(xiàn)在很多mvvm框架都用到了觀察者模式這個(gè)思想,觀察者模式又叫做發(fā)布—訂閱模式,它定義對(duì)象間的一種一對(duì)多的依賴關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對(duì)象都將得到通知和更新,觀察者模式提供了一個(gè)訂閱模型,其中對(duì)象訂閱事件并在發(fā)生時(shí)得到通知,這種模式是事件驅(qū)動(dòng)的編程基石,它有利益于良好的面向?qū)ο蟮脑O(shè)計(jì)
定義:對(duì)象間的一種一對(duì)多的依賴關(guān)系。
需求:當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生變化時(shí),所有依賴于他的對(duì)象都將得到通知。
優(yōu)點(diǎn):時(shí)間上的解耦,對(duì)象之間的解耦。
實(shí)現(xiàn):
指定好誰(shuí)充當(dāng)發(fā)布者;
給發(fā)布者添加一個(gè)緩存列表,用于存放回調(diào)函數(shù)以便通知訂閱者;
發(fā)布消息的時(shí)候,發(fā)布者會(huì)遍歷這個(gè)緩存列表,依次觸發(fā)里面存放的訂閱者回調(diào)函數(shù)。
下面舉個(gè)例子,比如我們給頁(yè)面中的一個(gè)dom節(jié)點(diǎn)綁定一個(gè)事件,其實(shí)就可以看做是一種觀察者模式:
document.body.addEventListener("click", function() { alert("Hello World") },false ) document.body.click() //模擬用戶點(diǎn)擊
總結(jié):在上面的例子中,需要監(jiān)聽用戶點(diǎn)擊 document.body 的動(dòng)作,但是我們是沒(méi)辦法預(yù)知用戶將在什么時(shí)候點(diǎn)擊的,因此我們訂閱了 document.body 的 click 事件,當(dāng) body 節(jié)點(diǎn)被點(diǎn)擊時(shí),body 節(jié)點(diǎn)便會(huì)向訂閱者發(fā)布 "Hello World" 消息。
五、策略模式
策略模式指的是定義一些列的算法,把他們一個(gè)個(gè)封裝起來(lái),目的就是將算法的使用與算法的實(shí)現(xiàn)分離開來(lái),避免多重判斷條件,更具有擴(kuò)展性。
下面也是舉個(gè)例子,現(xiàn)在超市有活動(dòng),vip為5折,老客戶3折,普通顧客沒(méi)折,計(jì)算***需要支付的金額,如果不使用策略模式,我們的代碼可能和下面一樣:
function Price(personType, price) { //vip 5 折 if (personType == 'vip') { return price * 0.5; } else if (personType == 'old'){ //老客戶 3 折 return price * 0.3; } else { return price; //其他都全價(jià) } }
在上面的代碼中,我們需要很多個(gè)判斷,如果有很多優(yōu)惠,我們又需要添加很多判斷,這里已經(jīng)違背了剛才說(shuō)的設(shè)計(jì)模式的六大原則中的開閉原則了,如果使用策略模式,我們的代碼可以這樣寫:
// 對(duì)于vip客戶 function vipPrice() { this.discount = 0.5; } vipPrice.prototype.getPrice = function(price) { return price * this.discount; } // 對(duì)于老客戶 function oldPrice() { this.discount = 0.3; } oldPrice.prototype.getPrice = function(price) { return price * this.discount; } // 對(duì)于普通客戶 function Price() { this.discount = 1; } Price.prototype.getPrice = function(price) { return price ; } // 上下文,對(duì)于客戶端的使用 function Context() { this.name = ''; this.strategy = null; this.price = 0; } Context.prototype.set = function(name, strategy, price) { this.name = name; this.strategy = strategy; this.price = price; } Context.prototype.getResult = function() { console.log(this.name + ' 的結(jié)賬價(jià)為: ' + this.strategy.getPrice(this.price)); } var context = new Context(); var vip = new vipPrice(); context.set ('vip客戶', vip, 200); context.getResult(); // vip客戶 的結(jié)賬價(jià)為: 100 var old = new oldPrice(); context.set ('老客戶', old, 200); context.getResult(); // 老客戶 的結(jié)賬價(jià)為: 60 var Price = new Price(); context.set ('普通客戶', Price, 200); context.getResult(); // 普通客戶 的結(jié)賬價(jià)為: 200
到此,相信大家對(duì)“JavaScript5大常用設(shè)計(jì)模式是什么”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!