關於W3Cschool定義的設計模式--常用的9種設計模式的介紹
一、設計模式
tip:每種設計模式,其實都是為了更高效的,更方便的解決在面對物件程式設計中所遇到的問題。
什麼是設計模式: 是一套經過反覆使用、多人知曉的、經過分類的、程式碼設計經驗的總結 為什麼使用設計模式: 為了程式碼的可重用性、讓程式碼更容易被他人理解、保證程式碼的可靠性。設計模式使程式碼的編寫真正的工程化;設計模式是軟體工程的基石脈絡,如同大廈的結構。 有哪些設計模式(來自W3Cschool,23種): 構造器模式,模組化模式,暴露模組模式,單例模式,中介者模式,原型模式,命令模式,外觀模式,工廠模式,Mixin模式,裝飾模式,亨元(Flyweight)模式,MVC模式,MVP模式,MVVM模式,組合模式,介面卡模式,外觀模式,觀察者模式,迭代器模式,惰性初始模式,代理模式,建造者模式. 二、工廠模式 看工廠模式我們首先要看幾個原理。 (1)new的原理: 1.建立了一個新的空物件。 2.將這個物件的__proto__和函式的prototype做連線。 3.將這個函式中的this改變,指向new新建立的物件。 4.檢測函式有沒有有返回物件,沒有返回物件,就返回new建立的物件。 (2)建構函式的原理、特徵和注意事項: 1.建構函式,構造自定義的函式,會在函式中使用this、找到被構造出來的物件。 2.隱患:一旦建構函式被直接執行,會錯誤的產生大量的全域性變數。 3.解決隱患:但是解決不了。 4.自我約束:建構函式不要直接執行。 5.防止誤操作:行業習慣:為了防止建構函式被直接執行,產生大量的全域性變數,一般將需要被構造的函式的首字母大寫,以此標誌建構函式和正常函式的區別。 工廠模式(建構函式模型)基礎語法:function Fn(){ this.name = "root"; } var f = new Fn() console.log(f.name)//root
單例模式
//單個例項,只有一個物件 //多次建立,返回同一個物件 function fn(){ if(!fn.obj) { //給函式新增一個屬性。 因為函式是全域性的,所以該屬性一旦新增一直存在; fn.obj = { name : “liyang" }; } return fn.obj; } var obj1 = new fn(); var obj2 = new fn(); console.log(obj1 == obj2); //例如我們建立一個資訊提示框,每一次執行toast方法,如果都建立一個新的元素,這樣做太浪費了。 //因此,我們採取單例模式,確保無論呼叫多少次toast方法,都只建立一個DOM元素。 //我們只控制它的顯示和隱藏,而不是每次建立每次銷燬。 function Toast(){ var div = document.createElement("div"); div.className = "box"; document.body.appendChild(div); setTimeout(function(){ div.remove(); },1000) } obtn.onclick = function(){ var a = new Toast(); var b = new Toast(); console.log(a == b) } function Toast(){ if(!Toast.div){ Toast.div = document.createElement("div"); Toast.div.className = "box"; document.body.appendChild(Toast.div); clearTimeout(Toast.div.timer); Toast.div.timer = setTimeout(function(){ Toast.div.style.display = "none"; },1000) }else{ Toast.div.style.display = "block"; clearTimeout(Toast.div.timer); Toast.div.timer = setTimeout(function(){ Toast.div.style.display = "none"; },1000) } return Toast; } obtn.onclick = function(){ var a = new Toast(); var b = new Toast(); console.log(a == b); }
單例模式-彈出框案例
// 單例模式 function Toast(){ // 第一次執行函式時給建構函式新增屬性為物件 if(!Toast.obj){ Toast.obj = {}; // 對物件進行加工 Toast.obj.dia = document.createElement("dialog"); Toast.obj.dia.innerHTML = "這是一個彈出框"; document.body.appendChild(Toast.obj.dia) } // 正常情況下,每次建立元素,都要立即顯示 Toast.obj.dia.style.display = "block"; // 過一定的時間後,隱藏元素 clearTimeout(Toast.obj.t) Toast.obj.t = setTimeout(() => { Toast.obj.dia.style.display = "none" }, 2000); // 覆蓋this的指向(覆蓋new出來的物件) return Toast.obj; } document.onclick = function(){ var t1 = new Toast() var t2 = new Toast() var t3 = new Toast() var t4 = new Toast() var t5 = new Toast() //這些被new出來的物件,都指向一個物件 console.log(t1 == t2)//true console.log(t3 == t2)//true console.log(t5 == t2)//true console.log(t4 == t2)//true console.log(t1 == t3)//true console.log(t3 == t5)//true }
四、組合模式
1、組合模式是用來組合物件的,一般應用於頁面,將物件按照一定的規律和關係組合,組成樹狀結構,類似於DOM元素中的樹狀結構,可以完成動態網頁的生成、建立元素和修改樣式。
2、將物件組合起來之後,可以實現:批量操作。
3、缺點:節省了操作,消耗了效能。
tip:組合模式最重要的是組合器。
// 最關鍵的是組合器: function ImagesStore( id ){ this.children = []; this.element = document.createElement("div"); this.element.id = id; document.body.appendChild(this.element) } ImagesStore.prototype = { constructor : ImagesStore, add:function( child ){ this.children.push( child ); this.element.appendChild( child.getElement() ); }, remove:function( child ){ for( var node, i=0; node = this.getChild(i); i++ ){ if( node === child ){ this.children.splice( i, 1 ); break; } } this.element.removeChild( child.getElement() ); }, getChild:function( i ){ return this.children[i]; }, show:function(){ this.element.style.border = 'solid 2px black'; for( var node, i=0; node = this.getChild(i); i++ ){ node.show(); } }, hide:function(){ for( var node, i=0; node = this.getChild(i); i++ ){ node.hide(); } this.element.style.border = 'none'; }, getElement:function(){ return this.element; } } function ImageItem( src ){ this.element = document.createElement("img"); this.element.src = src; this.element.className = "img-item"; } ImageItem.prototype = { constructor:ImageItem, add:function( child ){ console.log("這是子物件了,沒有新增功能"); }, remove:function( child ){ console.log("這是子物件了,沒有刪除功能"); }, getChild:function( i ){ console.log("這是子物件了,沒有獲取子物件功能"); }, show:function(){ this.element.style.border = 'solid 2px black'; }, hide:function(){ this.element.style.border = 'none'; }, getElement:function(){ return this.element; } } var box = new ImagesStore("box"); var xbox = new ImagesStore("xbox"); var img1 = new ImageItem("https://www.baidu.com/img/bd_logo1.png") var img2 = new ImageItem("https://www.baidu.com/img/bd_logo1.png") xbox.add(img1) xbox.add(img2) box.add(xbox) // box.remove(img1) // img1.show() box.show() // img1.add()
使用組合模式組織起來的物件具有出色的層次結構,每當對頂層組合物件執行一個操作的時候,實際上是在對整個結構進行深度優先的節點搜尋。但是這些優點都是用操作的代價換取的,比如每次頂級執行一次show方法,實際的操作就是整個樹形結構的節點都會被遍歷一次。但是組合物件的每個物件之間的耦合非常鬆散,可以簡單的操作處理複雜的結果。 簡單的說,組合模式是講一批子物件組織為樹形結構,一條頂層的命令會在操作樹中所有的物件。提高了程式碼的模組化程度,對於動態的HTML介面具有很強的適用性
五、觀察者模式
1、觀察者模式又叫釋出訂閱者模式:
(1)釋出者:主題物件,一般只有一個。
(2)接收者:訂閱者,多個,隨時新增和刪除。
(3)廣播通訊,一個物件釋出資訊,多個物件接收資訊,並做出對應處理。
2、觀察者模式的好處:
(1)支援簡單的廣播通訊,自動通知所有已經訂閱過的物件。
(2)頁面載入後目標物件很容易與觀察者存在一種動態關聯,增加了靈活性。
(3)目標物件與觀察者之間的抽象耦合關係能夠單獨擴充套件以及重用。
觀察者模式-案例-看孩子還是打麻將
function child(n){ this.name = n; this.type = function(){ return Math.random()>0.5? 0 : 1; } } function mather(n,c){ this.name = n; this.child = c; this.listen = function(t){ if(t==0){ console.log(this.child.name + "哭了,"+this.name+"看孩子") }else{ console.log(this.child.name + "睡了,"+this.name+"打麻將") } } } function father(n,c){ this.name = n; this.child = c; this.listen = function(t){ if(t==0){ console.log(this.child.name + "哭了,"+this.name+"看孩子") }else{ console.log(this.child.name + "睡了,"+this.name+"打麻將") } } } var c = new child("大寶"); var t = c.type(); var m = new mather('大寶媽',c); m.listen(t); var f = new father('大寶爸',c); f.listen(t);
六、代理模式
代理模式的應用場景:當我們需要代理一些別人的封裝好的功能或封裝好的元件,像另外一個封裝好的功能或元件傳送一些資料的時候,這時候我們沒法從這兩個功能之間去拿資料,這時候我們就可以寫一箇中間層或著代理,將這個資料暴露出來,我們就可以再次使用或者多次使用,甚至對這些資料進行修改。 用一句話總結代理模式:為其他物件提供代理,以控制這個物件的訪問; 舉個簡單的例子: 有一個小夥子想要送花給小姑娘,但是不好意思送,於是找到了快遞小哥,快遞小哥幫忙送花;在這裡快遞小哥就是代理!我們就像是那個快遞小哥,等於到程式猿身上要做的就是截獲資料。function girl(name){ this.name = name; } function boy(girl){ this.girl = girl; this.sendGift = function(gift){ alert("你好,漂亮的"+this.girl.name+",這是我送你的:"+gift); } } function porxyLitterBrother(girl){ this.girl = girl; this.send = function(gift){ this.g = gift; gift = "一個擁抱"; var b = new boy(girl); b.sendGift(gift); } this.init = function(){ console.log(this.g) } } var g = new girl("翠花"); var p = new porxyLitterBrother(g); p.send("鑽戒") p.init()
七、介面卡模式
介面卡模式就是將原本不具有某些功能的物件,在使用這些功能時,不出問題,並讓某些不具有特徵的屬性,變得特徵
demo:
電子工廠:手機,平板
手機:打電話,玩遊戲
平板:玩遊戲
測試模組只有一個:想能測平板又能測手機,還正確測試,不出問題
// 應用場景 // 讓某個不具有明顯特徵的功能,變得有特徵 function phone(){ this.name = "phone"; this.call = function(){ console.log(this.name + "可以打電話"); }; this.game = function(){ console.log(this.name + "可以打遊戲"); }; } function pad(){ this.name = "pad"; this.game = function(){ console.log(this.name + "可以打遊戲") } } function text(obj){ if(obj.call){ obj.call(); }else{ console.log(obj.name+"沒有打電話的功能"); }; if(obj.game){ obj.game(); }else{ consloe.log(obj.name+"沒有打遊戲的功能"); } } var p1 = new phone(); text(p1); var p2 = new pad(); text(p2);//介面卡的意義,多數應用在系統介面使用,也就是別人提供了一個功能,但要求傳入一個A型別的引數 //而我們手裡的資料是B型別的,如果我們想使用這個功能。那麼有兩種解決辦法: //第一,把自己的原始碼進行修改,讓B型別改為A型別,這是非常蠢的做法。 //第二,我們把B型別的資料進行一個包裝,讓它看起來符合型別A,這個包裝函式,就是介面卡。
八、抽象工廠模式
在工廠模式中,將多個例項的相同屬性或方法,再次抽象成一個公共物件,從公共物件上,再次創建出具體的例項。
demo:
造車廠:製造汽車
汽車需要車架子:輪子,引擎。
我們可以將相同的部分放一起,然後通過新增其他不同的零件,生產不行型號的車。
var f = (function (){ //抽象工廠模式主要就是這個公共物件,模具物件 function car(wheel,engine){ //內部配置函式,可以提供配置功能。 this.wheel = wheel; this.engine = engine; } return function(wheel , engine){ // 構造器, 負責建立物件。 return new car(wheel,engine); // 這是對外提供的介面,負責和外部需求連線。 } })(); var car1 = f("15","V8"); var car2 = f("13","V10"); //每次執行都會有這個公共物件,執行這個公共物件,獲得一個新的物件 console.log(car1);//一個新的car物件 console.log(car2);//一個新的car物件 console.log(car1 == car2);//false
//這種模式,就是所謂的抽象工廠模式。
九、策略模式
策略:計劃,規劃,預製要做的事情,不同的情況定製不同的計劃。
function fn(n){ if(n < 10 || n.length < 1){ return "0"+n }else{ return n; } } var num = fn(4); console.log(num)//04
總結:這就是我們平時常用的九種設計模式,每種設計模式其實都是見名識義,很多種設計模式我們也只會在寫一些大型的專案的時候我們才會使用,每一種設計模式我們都需要根據當前的實際需求,來判斷我們該使用哪種設計模式,使我們的程式碼解構更強。
&n