大型單頁應用裏,複雜度上升到必定程度時,沒有適當的設計模式進行降耦,後續的開發也難如下手。
而設計模式正是爲了降耦而存在。javascript
單例模式的核心是確保只有一個實例,而且提供全局訪問。java
知足「單一職責原則」 : 使用代理模式,不在構造函數中判斷是否已經建立過該單例;node
知足惰性原則ajax
彈出登錄窗口。算法
var getSingle = function (fn) { var res; return function() { return res || (res = fn.apply(this, arguments)); } } var createPopup() { var div = document.createElement('div'); div.innerHTML = "Login window"; div.style.display = "none"; document.body.appendChild(div); return div; } var createLoginPopup = getSingle(createPopup); //create popup div here by using a given function, 知足兩個原則 document.getElementById("loginBt").onclick = function() { var popup = createLoginPopup(); pop.style.display = "block"; }
/** * 構造一個動物的函數 */ function Animal(name, color){ this.name = name; this.color = color; this.getName = function(){ return this.name; } } // 實例一個對象 var cat = new Animal('貓', '白色'); console.log( cat.getName() );
function Person(){ } Person.prototype.name = "bill"; Person.prototype.address = "GuangZhou"; Person.sayName = function (){ alert(this.name); } var person1 = new Person(); var person2 = new Person(); //測試代碼 alert(person1.name); // bill alert(person2.name); // bill person1.sayName(); //bill person2.sayName(); //bill person1.name = "666"; alert(person1.name); // 666 alert(person2.name); // bill person1.sayName(); //666 person2.sayName(); //bill
/** * 混合模式 = 原型模式 + 構造函數模式 */ function Animal(name, color){ this.name = name; this.color = color; console.log( this.name + this.color) } Animal.prototype.getInfo = function(){ console.log('名稱:'+ this.name); } function largeCat(name, color){ Animal.call(null, name, color); this.color = color; } largeCat.prototype = create(Animal.prototype); function create (parentObj){ function F(){} F.prototype = parentObj; return new F(); }; largeCat.prototype.getColor = function(){ return this.color; } var cat = new largeCat("Persian", "白色"); console.log( cat )
工廠:函數內部產生b對象並返回。設計模式
1. function a(name){ var b = new object(); b.name = name; b.say = function(){ alert(this.name); } return b } 2. function Animal(opts){ var obj = new Object(); obj.name = opts.name; obj.color = opts.color; obj.getInfo = function(){ return '名稱:'+obj.name +', 顏色:'+ obj.color; } return obj; } var cat = Animal({name: '波斯貓', color: '白色'}); cat.getInfo();
簡單工廠模式的理念就是建立對象,對不一樣類的實例化;只須要建立一個對象,而後經過對這個對象大量的方法和屬性,並在最終將對象返回出來瀏覽器
//basketball base class var Baseketball = function(){ this.intro = 'baseketball is hotting at unitedstates'; } Baseketball.prototype = { getMember : function(){\ console.log('each team needs five players'); }, getBallSize : function(){ console.log('basketball is big'); } } //football base class var Football = function(){ this.intro = 'football is popular at all of the world'; } Football = function(){ getMember = function(){ }, getBallSize = function(){ } } //sport factory var SportsFactory = function(name){ switch(name){ case 'NBA': return new Baseketball(); case 'wordCup': return new Football(); } } //when you want football var football = SportsFactory('wordCup'); console.log(football); console.log(football.intro); football.getMember();
裝飾者模式緩存
定義一個個能夠相互替換的算法,而且把他們封裝起來。app
動畫實現不一樣的緩動效果。函數
通常分爲兩個部分:策略類於環境類。策略類用於封裝各類算法,而且負責具體的計算過程; 環境類負責接收用戶的請求,而且把請求委託給某一個策略類。由於各個策略類實現的算法和計算的結果不一樣,而環境類調用策略類的方法倒是相同的,這就體現了多態性。要想實現不一樣的算法,只須要替換環境類中的策略類便可。
在js中,咱們沒必要構造策略類,可直接使用函數做爲策略對象。
示例
var strategies = { "s1": function() { //algo 1 }, "s2": function() { //algo 2 }, "s3": function() { //algo 3 } } var someContext = new SomeConext(); someContext.start("s1"); //using s1 to calculate //someContext.add("s1"); or add s1 as a rule for validation
也可譯爲門面模式。它爲子系統中的一組接口提供一個一致的界面, Facade模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。引入外觀角色以後,使用者只須要直接與外觀角色交互,使用者與子系統之間的複雜關係由外觀角色來實現,從而下降了系統的耦合度。
好比在家要看電影,須要打開音響,再打開投影儀,再打開播放器等等,引入外觀角色以後,只須要調用「打開電影設備」方法就能夠。外觀角色封裝了打開投影儀等操做,給使用者提供更容易使用的方法。
在形式上,外觀模式在javascript中就像這樣:
function a(x){ // do something } function b(y){ // do something } function ab( x, y ){ a(x); b(y); }
下面的一個例子,把阻止冒泡和阻止默認事件放到了外觀角色中:
var N = window.N || {}; N.tools = { stopPropagation : function( e ){ if( e.stopPropagation ){ e.stopPropagation(); }else{ e.cancelBubble = true; } }, preventDefault : function( e ){ if( e.preventDefault ){ e.preventDefault(); }else{ e.returnValue = false; } }, stopEvent : function( e ){ N.tools.stopPropagation( e ); N.tools.preventDefault( e ); }
外觀模式在javascript的應用主要能夠分爲兩類,某塊代碼反覆出現,好比函數a的調用基本都出如今函數b的調用以前,那麼能夠考慮考慮將這塊代碼使用外觀角色包裝一下來優化結構。還有一種就是對於一些瀏覽器不兼容的API,放置在外觀內部進行判斷,處理這些問題最好的方式即是將跨瀏覽器差別所有集中放置到一個外觀模式實例中來提供一個對外接口。
代理模式的定義:爲其餘對象提供一種代理以控制對這個對象的訪問。在某些狀況下,一個對象不適合或者不能直接引用另外一個對象,而代理對象能夠在客戶端和目標對象之間起到中介的做用。
虛擬代理是把一些開銷很大的對象,延遲到真正須要它的時候纔去建立執行
//圖片加載 let imageEle = (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(){ imageEle.setSrc(this.src); }; return { setSrc:function(src){ img.src = src; imageEle.setSrc('loading.gif'); } } })(); proxy.setSrc('example.png');
若是有一個功能須要頻繁進行請求操做,這樣開銷比較大,能夠經過一個代理函數收集一段時間內請求數據,一次性發出
//上傳請求 let upload = function(ids){ $.ajax({ data: { id:ids } }) } //代理合並請求 let proxy = (function(){ let cache = [], timer = null; return function(id){ cache[cache.length] = id; if(timer) return false; timer = setTimeout(function(){ upload(cache.join(',')); clearTimeout(timer); timer = null; cache = []; },2000); } })(); // 綁定點擊事件 let checkbox = document.getElementsByTagName( "input" ); for(var i= 0, c; c = checkbox[i++];){ c.onclick = function(){ if(this.checked === true){ proxy(this.id); } } }
緩存代理能夠做爲一些開銷大的運算結果提供暫時的存儲,下次運算時,若是傳遞進來的參數跟以前一致,則能夠直接返回前面存儲的運算結果
//計算乘積 let mult = function(){ let result = 1; for(let i = 0,len = arguments.length;i < len;i++){ result*= arguments[i]; } return result; } //緩存代理 let proxy = (function(){ let cache = {}; reutrn function(){ let args = Array.prototype.join.call(arguments,','); if(args in cache){ return cache[args]; } return cache[args] = mult.apply(this,arguments); } })();
1.優勢:代理模式能將代理對象與被調用對象分離,下降了系統的耦合度。代理模式在客戶端和目標對象之間起到一箇中介做用,這樣能夠起到保護目標對象的做用。代理對象也能夠對目標對象調用以前進行其餘操做。
2.缺點:增長了系統的複雜度
觀察者模式
/** * 模塊模式 = 封裝大部分代碼,只暴露必需接口 */ var Car = (function(){ var name = '法拉利'; function sayName(){ console.log( name ); } function getColor(name){ console.log( name ); } return { name: sayName, color: getColor } })(); Car.name(); Car.color('紅色');