摘要:在類似于這樣的面向?qū)ο笳Z(yǔ)言中,抽象類的使用在這個(gè)設(shè)計(jì)模式中非常重要。假設(shè)系統(tǒng)中存在大量類似的對(duì)象而導(dǎo)致內(nèi)存消耗過(guò)高,享元模式就非常有用了。享元模式包含兩種狀態(tài)即屬性內(nèi)部狀態(tài)存儲(chǔ)于對(duì)象內(nèi)部。不過(guò)與享元模式不同的是它不會(huì)區(qū)分內(nèi)部狀態(tài)和外部狀態(tài)。
模式8-模版方法模式
模版方法模式是一種基于繼承的設(shè)計(jì)模式。主要由兩部分構(gòu)成:
抽象父類:包含子類的算法框架和一些通用的具體方法;
具體實(shí)現(xiàn)的子類: 包含對(duì)于父類中抽象方法的實(shí)現(xiàn),繼承父類的整個(gè)算法實(shí)現(xiàn)方法,并且可以重寫父類中的方法。
在類似于java這樣的面向?qū)ο笳Z(yǔ)言中,抽象類的使用在這個(gè)設(shè)計(jì)模式中非常重要。因?yàn)樵诰幾g的時(shí)候會(huì)對(duì)繼承抽象類的子類進(jìn)行檢測(cè),要求必須要對(duì)抽象方法進(jìn)行實(shí)現(xiàn)。然而在javascript中沒(méi)有類型檢查,所以要保證子類實(shí)現(xiàn)了所有抽象父類的抽象方法,可以在運(yùn)行時(shí)進(jìn)行檢測(cè),即讓抽象方法拋出錯(cuò)誤。
示例:
var Beverage = function() {} Beverage.prototype.boilWater = function(){ console.log("boil water"); } Beverage.prototype.brew = function (){ throw new Error("you must define function brew"); } Beverage.prototype.pourInCup = function (){ throw new Error("you must define function pourInCup"); } Beverage.prototype.addCondiments = function (){ throw new Error("you must define function addCondiments"); } Beverage.prototype.customerWantsCondiments = function(){ throw new Error("you must define function customerWantsCondiments"); } //泡飲料的順序和步驟是定的,算是一個(gè)子類通用的算法 Beverage.prototype.init = function(){ this.boilWater(); this.brew(); this.pourInCup(); if(this.customerWantsCondiments){ this.addCondiments(); } }; var Tea = function(){}; Tea.prototype = new Beverage(); Tea.prototype.brew = function(){ console.log("brew-up"); } Tea.prototype.pourInCup = function(){ console.log("pour tea"); } Tea.prototype.addCondiments = function(){ console.log("add sugar and milk"); } Tea.prototype.customerWantsCondiments = function() { return window.confirm("Do you need condiments?"); } var tea = new Tea(); tea.init(); var Coffee = function(){}; Coffee.prototype = new Beverage(); Coffee.prototype.brew = function(){ console.log("brew coffee"); } Coffee.prototype.pourInCup = function(){ console.log("pour coffee"); } Coffee.prototype.addCondiments = function(){ console.log("add lemon"); } Coffee.prototype.customerWantsCondiments = function() { return window.confirm("Do you need condiments?"); } var coffee = new Coffee(); coffee.init();
這個(gè)設(shè)計(jì)模式是有利于系統(tǒng)的拓展的,并且符合開放-封閉原則。另外,我們?cè)趈s中不一定非要使用繼承的方式來(lái)完成這個(gè)設(shè)計(jì)模式,也可以通過(guò)傳入高階函數(shù)作為參數(shù)來(lái)實(shí)現(xiàn),用以替代父類中的抽象函數(shù)。
模式9-享元模式
享元模式是為了優(yōu)化性能而存在的。假設(shè)系統(tǒng)中存在大量類似的對(duì)象而導(dǎo)致內(nèi)存消耗過(guò)高,享元模式就非常有用了。
享元模式包含兩種狀態(tài)(即屬性):
內(nèi)部狀態(tài):
-存儲(chǔ)于對(duì)象內(nèi)部。 -可以被一些對(duì)象共享。 -獨(dú)立于具體的場(chǎng)景,通常不會(huì)改變。
外部狀態(tài):
取決于具體的場(chǎng)景,并根據(jù)場(chǎng)景而變化,外部狀態(tài)不能被共享。
剝離了外部狀態(tài)的對(duì)象成為共享對(duì)象,外部狀態(tài)在必要時(shí)被傳入共享對(duì)象來(lái)組裝成一個(gè)完整的對(duì)象。系統(tǒng)中可能存在的最大享元對(duì)象個(gè)數(shù)等于不同內(nèi)部狀態(tài)的組合數(shù)。
示例:
//這個(gè)享元只有一個(gè)內(nèi)部狀態(tài) var Model = function( sex ){ this.sex = sex; }; Model.prototype.takePhoto = function(){ console.log( "sex= " + this.sex + " underwear=" + this.underwear); }; //分別創(chuàng)建一個(gè)男模特對(duì)象和一個(gè)女模特對(duì)象: var maleModel = new Model( "male" ), femaleModel = new Model( "female" ); //給男模特依次穿上所有的男裝,并進(jìn)行拍照: //注:這里我們是在需要的時(shí)候才傳入外部狀態(tài) for ( var i = 1; i <= 50; i++ ){ maleModel.underwear = "underwear" + i; maleModel.takePhoto(); }; //同樣,給女模特依次穿上所有的女裝,并進(jìn)行拍照: for ( var j = 1; j <= 50; j++ ){ femaleModel.underwear = "underwear" + j; femaleModel.takePhoto(); };
應(yīng)用:
文件上傳,只根據(jù)上傳組件的不同來(lái)(使用工廠)新建uploader對(duì)象(只含有一個(gè)uploadType內(nèi)部狀態(tài),同樣uploadType的被共享),而文件信息等儲(chǔ)存在外部,只當(dāng)需要(如刪除文件)時(shí)才(通過(guò)uploadManager)將外部狀態(tài)傳入內(nèi)部。
對(duì)象池是另一種性能優(yōu)化的方案,其思想是創(chuàng)建一個(gè)池子用來(lái)存放空閑對(duì)象,當(dāng)需要使用該對(duì)象時(shí)從池子中取,如果沒(méi)有則創(chuàng)建,用完之后放回。不過(guò)與享元模式不同的是它不會(huì)區(qū)分內(nèi)部狀態(tài)和外部狀態(tài)。
模式10-職責(zé)鏈模式
思想是將可能處理請(qǐng)求的對(duì)象連成一個(gè)鏈,請(qǐng)求者只需知道第一個(gè)對(duì)象,然后將請(qǐng)求沿著鏈依次傳遞直到遇到可以處理該請(qǐng)求的對(duì)象。這就使得請(qǐng)求發(fā)出著和請(qǐng)求接受者之間解耦,也就是說(shuō)請(qǐng)求發(fā)出者不必知道哪個(gè)對(duì)象可以處理請(qǐng)求,避免了在一個(gè)函數(shù)中使用大量的if else判斷。
示例:
var handler1 = function(params){ if(params === true) { // check condition console.log("request solved by handler1"); } else { return false; // condition not valid } } var handler2 = function(params){ if(params === true) { // check condition console.log("request solved by handler2"); } else { return false; // condition not valid } } var handler3 = function(params){ if(params === true) { // check condition console.log("request solved by handler3"); } else { return false; // condition not valid } } var Chain = function( fn ){ this.fn = fn; this.successor = null; }; Chain.prototype.setNextSuccessor = function( successor ){ return this.successor = successor; }; Chain.prototype.passRequest = function(){ var ret = this.fn.apply( this, arguments ); if ( ret === false ){ return this.successor && this.successor.passRequest.apply( this.successor, arguments ); } return ret; }; //調(diào)用next函數(shù)可以手動(dòng)傳遞請(qǐng)求,用于異步處理 Chain.prototype.next= function(){ return this.successor && this.successor.passRequest.apply( this.successor, arguments ); }; var chain1 = new Chain(handler1); var chain2 = new Chain(handler2); var chain3 = new Chain(handler3); //指定節(jié)點(diǎn)在職責(zé)鏈中的順序 chain1.setNextSuccessor(chain2); chain2.setNextSuccessor(chain3); //把請(qǐng)求傳遞給第一個(gè)節(jié)點(diǎn): chain1.passRequest(someParams);
職責(zé)鏈模式使得各個(gè)鏈結(jié)點(diǎn)之間可以拆分重組,便于插入或刪除結(jié)點(diǎn)。并且請(qǐng)求不一定要從第一個(gè)結(jié)點(diǎn)開始。同時(shí),要避免職責(zé)鏈過(guò)長(zhǎng)帶來(lái)的性能問(wèn)題。
另外,可以利用js的函數(shù)式特性將函數(shù)“鏈接”起來(lái)實(shí)現(xiàn)職責(zé)鏈模式,即為Function對(duì)象的原型添加after函數(shù)。
應(yīng)用:
不同瀏覽器文件上傳控件的選擇,DOM事件冒泡等。
模式11-中介者模式
中介者模式是為了解除對(duì)象之間的強(qiáng)耦合關(guān)系,所有對(duì)象都通過(guò)中介者通信而不再相互引用。
示例:
假設(shè)一個(gè)網(wǎng)頁(yè)中幾個(gè)DOM元素的值共同決定一個(gè)按鈕的有效性,
例如input必須有效,select1和select2必須選擇
傳統(tǒng)的方法中,我們需要為這三個(gè)DOM元素添加值改變監(jiān)聽函數(shù),并且在每個(gè)監(jiān)聽函數(shù)中都要堅(jiān)持另外兩個(gè)DOM的值。這時(shí),如果我們需要加入一個(gè)input2,就需要將所有其他DOM元素的監(jiān)聽函數(shù)進(jìn)行修改。
如果使用中介者模式,我們可以創(chuàng)建一個(gè)mediator對(duì)象,并且提供一個(gè)向其發(fā)送消息的借口。然后,我們?yōu)槠渌鸇OM創(chuàng)建的監(jiān)聽函數(shù)中只需要向中介者發(fā)送一個(gè)消息并且把this作為參數(shù)傳遞告訴中介者是誰(shuí)發(fā)的消息。而中介者的實(shí)現(xiàn)中,只需要對(duì)收到的不同消息進(jìn)行處理即可,如果需要添加新的關(guān)聯(lián)DOM,也只需要稍稍修改mediator的代碼而不會(huì)需要修改其他DOM的監(jiān)聽函數(shù)了。
總之,中介者模式使對(duì)象之間的網(wǎng)狀引用關(guān)系變成了一對(duì)多的關(guān)系,滿足一個(gè)對(duì)象盡可能少地了解其他對(duì)象的原則。但是該模式引入了中介者對(duì)象,也會(huì)造成一些內(nèi)存消耗。
模式12-裝飾者模式
裝飾者模式用于動(dòng)態(tài)地給對(duì)象增加職責(zé)。
為了保證在執(zhí)行原函數(shù)和this指向發(fā)生改變,我們需要引入代理函數(shù)。代理函數(shù)的功能是在原函數(shù)執(zhí)行之前或之后,執(zhí)行另外的函數(shù)。
示例:
使用AOP,改變Function對(duì)象的原型
Function.prototype.before = function(beforefn){ var _self = this; return function(){ beforefn.apply(this, arguments); //確保this指向不變 this.apply(this, arguments); } } var func1 = function(){ alert("1"); } func1 = func1.before(function(){ alert("0"); }); func1(); //先輸出0再輸出1
不改變?cè)偷淖龇ǎ?/p>
var before = function(fn, beforefn){ return function() { beforefn.apply(this, arguments); fn.apply(this, arguments); } } func1 = before(func1, function(){alert("0")});
應(yīng)用:
對(duì)于某些用戶操作(如單擊按鈕)數(shù)據(jù)統(tǒng)計(jì)上報(bào),改變函數(shù)的arguments對(duì)象(如ajax傳數(shù)據(jù)時(shí)添加屬性),表單驗(yàn)證和提交功能的分離等。
這個(gè)設(shè)計(jì)模式使得開發(fā)人員在開發(fā)框架時(shí)可以只考慮對(duì)象的穩(wěn)定和基礎(chǔ)功能,其他需要添加的個(gè)性功能可以被動(dòng)態(tài)添加。它不同于代理模式的是,代理模式的目的是為了控制對(duì)對(duì)象的訪問(wèn)或添加一些功能,而裝飾者模式則是為了為對(duì)象動(dòng)態(tài)添加功能,并且通常會(huì)形成一條長(zhǎng)長(zhǎng)的裝飾鏈。
值得注意的是,裝飾者模式返回的是一個(gè)新的函數(shù),因此原函數(shù)上的屬性會(huì)消失。同時(shí),裝飾鏈也疊加了函數(shù)的作用域,過(guò)長(zhǎng)則會(huì)對(duì)性能產(chǎn)生影響。
模式13-狀態(tài)模式
狀態(tài)模式使得一個(gè)對(duì)象在其狀態(tài)改變時(shí)改變其行為。也就是說(shuō),在不同狀態(tài)下調(diào)用同一個(gè)名字的函數(shù)其函數(shù)功能是不同的。
在狀態(tài)模式中,一般有兩類對(duì)象:context和狀態(tài)類。context構(gòu)造函數(shù)中應(yīng)該實(shí)例化所有的狀態(tài)類并作為context的屬性,以便context調(diào)用狀態(tài)類中的方法。而狀態(tài)類的構(gòu)造函數(shù)應(yīng)該以context作為參數(shù),以便可以調(diào)用context中改變其state值的接口來(lái)對(duì)其進(jìn)行賦值。
例如一個(gè)擁有不同光強(qiáng)度的燈的控制代碼示例:
var Light = function(){ this.offLightState = new OffLightState( this ); // 持有狀態(tài)對(duì)象的引用 this.weakLightState = new WeakLightState( this ); this.strongLightState = new StrongLightState( this ); this.superStrongLightState = new SuperStrongLightState( this ); this.button = null; }; Light.prototype.init = function(){ var button = document.createElement( "button" ), self = this; this.button = document.body.appendChild( button ); this.button.innerHTML = "開關(guān)"; this.currState = this.offLightState; this.button.onclick = function(){ self.currState.buttonWasPressed(); } }; //接下來(lái)就要定義各種狀態(tài)類 var OffLightState = function( light ){ this.light = light; }; OffLightState.prototype.buttonWasPressed = function(){ console.log( "弱光" ); this.light.setState( this.light.weakLightState ); }; // 其他狀態(tài)類似,都需要定義一個(gè)buttonWasPressed方法,略
該設(shè)計(jì)模式優(yōu)點(diǎn)是將對(duì)象的狀態(tài)跟對(duì)應(yīng)的方法一起封裝在一個(gè)類里,使得狀態(tài)的管理如添加刪除等更easy。狀態(tài)模式與策略模式很相似,都是有context和一些策略/狀態(tài)類。不同點(diǎn)是策略類之間是平行的,用戶需要知道他們的不同來(lái)選擇調(diào)用,而狀態(tài)類之間的狀態(tài)轉(zhuǎn)換關(guān)系是早就規(guī)定好的,用戶也不必知道其內(nèi)部不同點(diǎn)。
JavaScript版本的狀態(tài)機(jī)(在js中狀態(tài)類不一定需要通過(guò)類來(lái)創(chuàng)建,可以使用顯式的對(duì)象):
var Light = function(){ this.currState = FSM.off; // 設(shè)置當(dāng)前狀態(tài) this.button = null; }; Light.prototype.init = function(){ var button = document.createElement( "button" ), self = this; button.innerHTML = "已關(guān)燈"; this.button = document.body.appendChild( button ); this.button.onclick = function(){ self.currState.buttonWasPressed.call( self ); } }; var FSM = { off: { buttonWasPressed: function(){ console.log( "關(guān)燈" ); this.button.innerHTML = "下一次按我是開燈"; this.currState = FSM.on; } }, on: { buttonWasPressed: function(){ console.log( "開燈" ); this.button.innerHTML = "下一次按我是關(guān)燈"; this.currState = FSM.off; }; var light = new Light(); light.init();
模式14-適配器模式
適配器模式的作用就是轉(zhuǎn)換不兼容的接口。
示例:
var getStudents = function(){ //這個(gè)函數(shù)返回一個(gè)對(duì)象的數(shù)組 var arr = [ {"id": 0, "name": "LiLei" }, {"id": 1, "name": "HanMeimei" } ]; } var printStudents = function(fn){ var params = fn(); for(var i = 0; i < params.length; i++){ console.log(params[i].name + ":" + params[i].id); } } printStudents(getStudents); //倘若現(xiàn)在提供學(xué)生信息的函數(shù)變了,返回值類型也變了: var getStudentsObj = function () { var stu = { "0": "LiLei", "1": "HanMeimei" }; } //adaptor var stuAdapter = function(oldfn){ var newRes = {}; var stu = oldfn(); for(var i = 0; i < stu.length; i++) { newRes[stu[i].id] = stu[i].name; } return function(){ return newRes; } } printStudent(stuAdapter(oldfn));
裝飾者模式與代理模式的結(jié)構(gòu)與適配器模式很像,都是用一個(gè)對(duì)象來(lái)包裝另一個(gè)對(duì)象,但不同點(diǎn)仍然是他們的意圖。適配者模式只是轉(zhuǎn)換接口,不需要知道對(duì)象的具體實(shí)現(xiàn)。
P.s. 本文總結(jié)自《JavaScript設(shè)計(jì)模式與開發(fā)實(shí)踐》,曾探著
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/81086.html
摘要:在中使用在中使用腳本有兩種方式一種是嵌入在中的腳本,另一種是引入外部的腳本。二者并行執(zhí)行,不會(huì)造成阻塞。字符編碼,較少使用。放置的位置將腳本放在標(biāo)簽前是最佳的。小結(jié)把插入到頁(yè)面中要使用元素。延遲腳本總是按照指定它們的順序執(zhí)行。 在 HTML 中使用 JavaScript 在html中使用JavaScript腳本有兩種方式一種是嵌入在HTML中的腳本,另一種是引入外部的腳本。兩種方式都離...
摘要:創(chuàng)建對(duì)象中,創(chuàng)建對(duì)象的基本模式有三種。因此,在設(shè)計(jì)構(gòu)造函數(shù)時(shí),需要進(jìn)行慎重考慮。因此在中,這種問(wèn)題被稱作繼承破壞封裝。靜態(tài)成員每個(gè)只有一份,直接通過(guò)類對(duì)象進(jìn)行訪問(wèn)。 什么是封裝 找工作時(shí)一些公司給了offer后我就想知道真正拿到手的是多少,畢竟賦稅繁重。但各種稅也好,五險(xiǎn)一金也好我實(shí)在是弄不清楚,于是我就會(huì)在網(wǎng)上的一些稅后收入計(jì)算器上進(jìn)行計(jì)算,只需要填寫一些基本信息,比如稅前收入,所...
摘要:元素向頁(yè)面中插入的主要方法就是使用元素。這個(gè)屬性的用途是表明腳本在執(zhí)行時(shí)不會(huì)影響頁(yè)面的構(gòu)造。因此,在元素中設(shè)置屬性,相當(dāng)于告訴瀏覽器立即下載,但延遲執(zhí)行?;祀s模式會(huì)讓的行為與包含非標(biāo)準(zhǔn)特性的相同,而標(biāo)準(zhǔn)模式則讓的行為更接近標(biāo)準(zhǔn)行為。 元素 向html頁(yè)面中插入js的主要方法就是使用元素。使用元素的方式有兩種:直接在頁(yè)面中嵌入js代碼和包含外部js文件。直接在頁(yè)面中嵌入js代碼如下: ...
摘要:構(gòu)造函數(shù)對(duì)于被實(shí)例化的,我們稱之為構(gòu)造函數(shù),及使用關(guān)鍵字調(diào)用的,對(duì)于它們來(lái)說(shuō),會(huì)被改變,指向?qū)嵗I侠踝尤仲x上屬性通過(guò)關(guān)鍵字創(chuàng)建實(shí)例,改變函數(shù)內(nèi)部指向注解通過(guò)這個(gè)栗子,我們可以看出,通過(guò)創(chuàng)建構(gòu)造函數(shù)的實(shí)例,使得的指向改變,指向了實(shí)例本身。 用栗子說(shuō)this Bug年年有,今年特別多 對(duì)于JavaScript這么靈活的語(yǔ)言來(lái)說(shuō),少了this怎么活! function ...
摘要:方法始終從前向后找參數(shù)接收兩個(gè)參數(shù),第一個(gè)參數(shù)可以是一個(gè)對(duì)象或者一個(gè)字符串這個(gè)字符串不會(huì)轉(zhuǎn)換成正則表達(dá)式,第二個(gè)參數(shù)可以是一個(gè)字符串或者一個(gè)函數(shù)。要想替換所有子字符串,唯一的辦法就是提供一個(gè)正則表達(dá)式,而且要指定全局標(biāo)志標(biāo)志。 字符串的模式匹配方法 match() 參數(shù):只接受一個(gè)參數(shù),要么是一個(gè)正則表達(dá)式,要么是一個(gè)RegExp()對(duì)象。 返回:數(shù)組。數(shù)組中的第一項(xiàng)是與整個(gè)模式匹配的...
閱讀 2203·2021-11-22 13:52
閱讀 1068·2021-11-17 09:33
閱讀 2783·2021-09-01 10:49
閱讀 2909·2019-08-30 15:53
閱讀 2715·2019-08-29 16:10
閱讀 2485·2019-08-29 11:31
閱讀 1481·2019-08-26 11:40
閱讀 1966·2019-08-26 10:59