JavaScript設(shè)計模式手寫示例講解
想分享的幾種設(shè)計模式
目前模式:工廠模式,單例模式,適配器模式,裝飾者模式,建造者模式
建造者模式
簡介:建造者模式(builder pattern)比較簡單,它屬于創(chuàng)建型模式的一種。
白話:4個部分:有個產(chǎn)品,有個工廠可以造產(chǎn)品,有個設(shè)計師指揮造多少,有個人想買產(chǎn)品。
買產(chǎn)品的用戶不介意產(chǎn)品制造流程,只需要產(chǎn)品!
function Cola() { this.sugar = '50g', this.water = '100g' } function Packing() { // 第一種打包方式 this.createPkg = function(){ console.log('創(chuàng)建可樂外皮') } this.pushCola = function() { console.log('可樂倒進瓶子') } this.complete = function() { var cola = new Cola() cola.complete = true return cola } this.init = function() { this.createPkg() // 創(chuàng)建外皮 this.pushCola() // 倒進瓶子 //還可以增加其他步驟 return this.complete() // 制作完成 } } function greenPacking() { //綠皮可樂打包方式 this.createPkg = function(){ console.log('創(chuàng)建green可樂外皮') } this.pushCola = function() { console.log('可樂倒進green瓶子') } this.complete = function() { var cola = new Cola() cola.complete = true return cola } this.init = function() { this.createPkg() // 創(chuàng)建外皮 this.pushCola() // 倒進瓶子 //還可以增加其他步驟 return this.complete() // 制作完成 } } function Boss() { this.createCola = function(packType) { const pack = new window[packType] this.product = pack.init() //完整產(chǎn)品產(chǎn)出 } this.getCola = function(packType) { this.createCola(packType); return this.product } } const boss = new Boss() var UserCola = boss.getCola('greenPacking') // UserCola.complete === true
其他東西都不要,只要最后生產(chǎn)好的Cola,有sugar,有water。
關(guān)鍵在于Boss 函數(shù)中,擔(dān)任一個整合的職責(zé)
同樣的Boss函數(shù),我可以通過更換Packing函數(shù),打包方式,獲得不同樣式的Cola。
通過給getCola函數(shù)傳入不同想要的參數(shù),獲得不同的最終產(chǎn)品。實現(xiàn)了可插拔的函數(shù)結(jié)構(gòu)。
裝飾者模式
裝飾者提供比繼承更有彈性的替代方案。 裝飾者用用于包裝同接口的對象,不僅允許你向方法添加行為,而且還可以將方法設(shè)置成原始對象調(diào)用(例如裝飾者的構(gòu)造函數(shù))。
裝飾者用于通過重載方法的形式添加新功能,該模式可以在被裝飾者前面或者后面加上自己的行為以達到特定的目的。
好處:
裝飾者是一種實現(xiàn)繼承的替代方案。當腳本運行時,在子類中增加行為會影響原有類所有的實例,而裝飾者卻不然。取而代之的是它能給不同對象各自添加新行為。參考前端手寫面試題詳細解答
function iwatch () { this.battery = 100; this.getBattery = function() { console.log(this.battery) } } iwatch.prototype.getNewPart = function(part) { this[part].prototype = this; //把this對象上的屬性 指向 新對象的prototype return new this[part]; //返回一個新對象,不修改原對象,新增了新對象的屬性 } iwatch.prototype.addNetwork = function() { this.network = function() { console.log('network') } } iwatch.prototype.addSwim = function() { this.swim = function() { console.log('swim') } } var watch = new iwatch(); watch.getBattery(); // 100 watch = watch.getNewPart('addNetwork'); // 添加新行為,network() watch = watch.getNewPart('addSwim'); // 既有network方法,也有swim方法
在 ES7 中引入了@decorator 修飾器的提案,參考阮一峰的文章。
@testable class MyTestableClass { // ... } function testable(target) { target.isTestable = true; } MyTestableClass.isTestable // true
直接可以使用,裝飾器行為
@decorator class A {} // 等同于 class A {} A = decorator(A) || A;
工廠模式
一個工廠能生產(chǎn)好多不同的產(chǎn)品,最常見的工廠函數(shù)就是jQ的$()函數(shù),每一個函數(shù)的結(jié)果都是一個需要的產(chǎn)品。
function Product(name) { this.name = name; } Product.prototype.init = function () { console.log('init'); } Product.prototype.go = function () { console.log('go'); } function Factory () { } Factory.prototype.add = function(name) { return new Product(name); } //use let f = new Factory(); let a = f.add('a'); console.log(a.name); a.init(); a.go();
適配器模式
Adapter,將一個類(對象)的接口(方法或者屬性)轉(zhuǎn)化為另一個接口,以滿足用戶需求,使類(對象)之間接口的不兼容問題通過適配器得以解決
function Person () { } Person.prototype.Say = function() { throw new Error("該方法必須被重寫!") } Person.prototype.Walk = function() { throw new Error("該方法必須被重寫!") } function Dog () { } Dog.prototype.Walk = function() { throw new Error("該方法必須被重寫!") } Dog.prototype.shout = function() { throw new Error("該方法必須被重寫!") } function PersonA () { Person.apply(this) } PersonA.prototype = new Person() PersonA.prototype.Say = function() { console.log('Person say') } PersonA.prototype.Walk = function() { console.log('Person Walk') } function DogBlack () { Dog.apply(this) } DogBlack.prototype = new Dog() DogBlack.prototype.Walk = function() { console.log('Dog Walk') } DogBlack.prototype.shout = function() { console.log('Dog Shout') } //現(xiàn)在希望Dog類也可以學(xué)會Say,并且多走幾步 function DogSayAdapter (DogClass) { Dog.apply(this) this.DogClass = DogClass } DogSayAdapter.prototype = new Dog() DogSayAdapter.prototype.Say = function() { this.DogClass.shout() } DogSayAdapter.prototype.Walk = function() { this.DogClass.Walk() this.DogClass.Walk() } var personA = new PersonA() var dogBlack = new DogBlack() var dogSay = new DogSayAdapter(dogBlack) personA.Say() personA.Walk() dogBlack.Walk() dogBlack.shout() dogSay.Say() dogSay.Walk()//walk * 2
適配器不只是函數(shù)接口,還有數(shù)據(jù)格式的適配
在前后端數(shù)據(jù)傳遞時,常用到適配器模式,也就是通俗易懂的格式化數(shù)據(jù),format函數(shù)等等
vue的computed計算屬性也是適配器模式的一種實現(xiàn)
const originData = [ { title: 'title', age: 18, content: ['123',321], callback: function(){ console.log(this) } }, { title: 'title2', age: 1, content: ['1',3], callback: function(){ console.log('title2') } } ] function dataAdapter(data) { return data.map(item => { return { title: item.title, content: item.content.join(','), init: item.callback } }) } var formatData = dataAdapter(originData)
e.g:原始data 的數(shù)據(jù)不滿足當前的要求,通過適配器,把數(shù)據(jù)格式化成想要的格式,對原始數(shù)據(jù)沒有改變
單例模式
function Simple (name) { this.name = name } Simple.prototype.go = function() { this.name = 'go' console.log(this.name) } //static靜態(tài)方法 Simple.getInstance = (function() { var ins return function(name){ if (!ins) { ins = new Simple(name) } return ins } })() let a = Simple.getInstance('a') // name: a let b = Simple.getInstance('b') // name: a b===a//true
非單例模式下,相同的new Simple()構(gòu)造函數(shù),不相等。
通過閉包只創(chuàng)建一次Simple實例,大家公用一個。
惰性單例模式
惰性和懶加載lazyload相似,延遲加載,或者說需要時再加載,不然一次加載過多,頻繁進行操作dom影響性能
盡管上述代碼有Simple.getInstance方法,可以在需要時再進行實例化,但仍然不是一個好的實現(xiàn)方式。
可以將惰性加載的部分提取出來。
e.g:
var simple = function(fn) { var instance; return function() { return instance || (instance = fn.apply(this, arguments)); } }; // 創(chuàng)建遮罩層 var createMask = function(){ // 創(chuàng)建div元素 var mask = document.createElement('div'); // 設(shè)置樣式 mask.style.position = 'fixed'; mask.style.top = '0'; ... ... document.body.appendChild(mask); // 單擊隱藏遮罩層 mask.onclick = function(){ this.style.display = 'none'; } return mask; }; // 創(chuàng)建登陸窗口 var createLogin = function() { // 創(chuàng)建div元素 var login = document.createElement('div'); // 設(shè)置樣式 login.style.position = 'fixed'; login.style.top = '50%'; ... ... login.innerHTML = 'login it'; document.body.appendChild(login); return login; }; document.getElementById('btn').onclick = function() { var oMask = simple(createMask)(); oMask.style.display = 'block'; var oLogin = simple(createLogin)(); oLogin.style.display = 'block'; }
總結(jié)
對五種常見常用的設(shè)計模式進行了學(xué)習(xí),這幾種很多時候都會用到,接下來還會繼續(xù)學(xué)習(xí)其他的18種設(shè)計模式,可能有的設(shè)計模式不一定在實際敲碼中使用,學(xué)了沒壞處,總能用得上嗷!
網(wǎng)上對于設(shè)計模式的文章,書籍層出不盡,但看得再多,不如自己理解,并且實際使用。很多時候是幾種設(shè)計模式融合在一起使用,如果不是自己去寫一遍,理解一遍,可能常見的設(shè)計模式都理解不了。這樣就太可惜了,發(fā)現(xiàn)干凈整潔的代碼,都說不出哪里好,就是看著舒服,順眼,運行速度快…
到此這篇關(guān)于JavaScript設(shè)計模式手寫示例講解的文章就介紹到這了,更多相關(guān)JS設(shè)計模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Bootstrap在線電子商務(wù)網(wǎng)站實戰(zhàn)項目5
這篇文章主要為大家分享了Bootstrap在線電子商務(wù)網(wǎng)站實戰(zhàn)項目,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-10-10動態(tài)的綁定事件addEventListener方法的使用
本文為大家介紹下動態(tài)的綁定事件的方法addEventListener的使用示例,不了解的朋友可以參考下2014-01-01解決一個微信號同時支持多個環(huán)境網(wǎng)頁授權(quán)問題
由于微信限制一個服務(wù)號只能配置一個網(wǎng)頁授權(quán)域名, 又不可能給每個環(huán)境單獨配一個服務(wù)號,這樣不僅需要成本而且很浪費資源,下面小編給大家?guī)砹私鉀Q一個微信號同時支持多個環(huán)境網(wǎng)頁授權(quán)問題,感興趣的朋友一起看看吧2019-08-08wangEditor富文本編譯器插件學(xué)習(xí)系列之工具欄配置
這篇文章主要給大家介紹了關(guān)于wangEditor富文本編譯器插件學(xué)習(xí)系列之工具欄配置的相關(guān)資料,wangEditor是一款基于原生JavaScript封裝,開源免費的富文本編輯器,支持常規(guī)的文字排版操作、插入圖片、插入視頻、插入代碼等功能,需要的朋友可以參考下2023-12-12TypeError: Cannot set properties of 
這篇文章主要介紹了TypeError: Cannot set properties of undefined (setting ‘xx‘)的問題,本文給大家分享完美解決方案,需要的朋友可以參考下2023-09-09網(wǎng)站導(dǎo)致瀏覽器崩潰的原因總結(jié)(多款瀏覽器) 推薦
對于訪客,如果登錄您網(wǎng)站,瀏覽器就立刻崩潰,我想這對誰都是無法容忍的,對此總結(jié)了網(wǎng)站導(dǎo)致瀏覽器崩潰的原因2010-04-04