轉(zhuǎn)載自:https://www.cnblogs.com/chenxygx/p/5754101.html
Javascript設(shè)計(jì)模式記錄,這個(gè)方面確實(shí)是沒寫過,工作中也沒有用到j(luò)s設(shè)計(jì)模式的地方。
prototype與面向?qū)ο笕∩?/p>
使用prototype原型繼承和使用面向?qū)ο?,都可以?shí)現(xiàn)閉包的效果。那么這兩個(gè)的選擇點(diǎn),就是方法會(huì)不會(huì)產(chǎn)生多個(gè)實(shí)例。
例如,我們需要做一個(gè)閉包數(shù)組,并給他提供一個(gè)添加方法。
1!(function () { 2//原型繼承寫法 3varValidator =function(){ 4this.cache = []; 5? ? }; 6Validator.prototype.add =function(item){ 7this.cache.push(item); 8? ? }; 9varvalidator =newValidator(),validatorr =new Validator();10validator.add("test1"); console.log(validator.cache);11validatorr.add("test2"); console.log(validatorr.cache);12//面向?qū)ο髮懛?3varValidator2 = {14? ? ? ? cache : [],15add :function(item){16this.cache.push(item);17? ? ? ? }18? ? };19Validator2.add("test3"); console.log(Validator2.cache);20Validator2.add("test4"); console.log(Validator2.cache);21})()
這兩種寫法都可以實(shí)現(xiàn)閉包,但是面向?qū)ο蟮膶懛?,只能存在一個(gè)。我們無法對(duì)他進(jìn)行初始化,而原型繼承寫法,我們則可以對(duì)他進(jìn)行初始化操作。
所以當(dāng),我們認(rèn)為這個(gè)方法,在整個(gè)程序中,是唯一的存在。我們可以使用面向?qū)ο蟮膶懛ǎ绻梢源嬖诙鄠€(gè),則使用prototype這種寫法。
調(diào)用父類構(gòu)造函數(shù)
繼承關(guān)系的兩個(gè)對(duì)象,在實(shí)例的過程中,可以通過修改指向,來調(diào)整調(diào)用構(gòu)造函數(shù)。
!(function () {
? ? varA =function (light) {
? ? ? ? this.light1 = light;
? ? };
? ? varB =function (light) {
? ? ? ? this.light = light;
? ? ? ? A.apply(this,arguments);//你需要手動(dòng)調(diào)用A的構(gòu)造方法? ? };
? ? //給B賦值的同時(shí),給A賦值B.prototype =new A();
? ? varC =newB(123);
? ? console.log(C.light);
? ? console.log(C.light1);
})()
單例模式
保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問它的全局訪問點(diǎn)。例如:線程池,全局緩存,登錄浮窗。
首先我們需要把單例的邏輯代碼單獨(dú)提取,然后使用惰性單例的方式,也就是返回方法。只有在點(diǎn)擊的時(shí)候,才會(huì)進(jìn)行執(zhí)行。
javascript的單例,跟類不一樣。無需創(chuàng)建多余的構(gòu)造函數(shù)這些,直接創(chuàng)建全局變量即可。
1!(function () { 2//管理單例的邏輯代碼,如果沒有數(shù)據(jù)則創(chuàng)建,有數(shù)據(jù)則返回 3vargetSingle =function(fn){//參數(shù)為創(chuàng)建對(duì)象的方法 4var result; 5returnfunction(){//判斷是Null或賦值 6returnresult || (result = fn.apply(this,arguments)); 7? ? ? }; 8? }; 9//創(chuàng)建登錄窗口方法10varcreateLoginLayer =function(){11vardiv = document.createElement('div');12div.innerHTML = '我是登錄浮窗';13div.style.display = 'none';14? ? ? ? document.body.appendChild(div);15return div;16? ? };17//單例方法18varcreateSingleLoginLayer = getSingle(createLoginLayer);1920//使用惰性單例,進(jìn)行創(chuàng)建21document.getElementById('loginBtn').onclick =function(){22varloginLayer = createSingleLoginLayer();23loginLayer.style.display = 'block';24? ? };25})()
策略模式
定義一系列的算法,把它們一個(gè)一個(gè)封裝起來。將算法的使用與算法的實(shí)現(xiàn)分離開來。
javascript的策略模式很簡(jiǎn)單,把算法直接定義成函數(shù)即可。
1!(function () { 2//定義算法方法 3varstrategies = { 4"S":function(salary){ 5returnsalary * 4; 6? ? ? ? }, 7"A":function(salary){ 8returnsalary * 3; 9? ? ? ? },10"B":function(salary){11returnsalary * 2;12? ? ? ? }13? ? };14//執(zhí)行算法15varcalculateBouns =function(level,salary){16return strategies[level](salary);17? ? };18console.log(calculateBouns('S',2000));19})()
undefined終止循環(huán)
(寫具體代碼之前,先記錄一個(gè)知識(shí)點(diǎn))。當(dāng)循環(huán)表達(dá)式為undefined時(shí),循環(huán)會(huì)終止。
!(function(){
? ? varcale = [1,2,3];
? ? for(vari= 0,validate;validate=cale[i];)
? ? {
? ? ? ? cale.shift();
? ? ? ? console.log(validate);
? ? }
})() //1,2,3
登錄驗(yàn)證表單
下面寫一個(gè)使用策略模式,制作的驗(yàn)證表單登錄效果。
傳統(tǒng)的表單登錄效果,會(huì)在提交后進(jìn)行一系列的判斷驗(yàn)證。這樣提交方法很龐大,而且缺少?gòu)椥?,?fù)用性也很差。
我們可以使用策略模式,來避免這些問題。
<form action="post" id="registerForm"><input type="text" name="userName"/><input type="text" name="password"/><input type="text" name="phoneNumber"/><button>提交</button></form>
!(function () {
? ? //定義驗(yàn)證規(guī)則,使用策略模式,直接通過 strategies[isNonEmpty]()可以訪問varstrategies = {
? ? ? ? isNonEmpty: function(value, errorMsg) {//不為空if(value === "") {
? ? ? ? ? ? ? ? return errorMsg;
? ? ? ? ? ? }
? ? ? ? },
? ? ? ? minLength: function(value, length, errorMsg) {//最小長(zhǎng)度if(value.length < length) {
? ? ? ? ? ? ? ? return errorMsg;
? ? ? ? ? ? }
? ? ? ? },
? ? ? ? isMobile: function(value, errorMsg) {//手機(jī)號(hào)碼格式if(!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
? ? ? ? ? ? ? ? return errorMsg;
? ? ? ? ? ? }
? ? ? ? }
? ? };
? ? //創(chuàng)建驗(yàn)證邏輯,使用閉包定義全局?jǐn)?shù)組,存放驗(yàn)證方法varValidator =function () {
? ? ? ? this.cache = [];
? ? };
? ? //添加驗(yàn)證邏輯方法,參數(shù):元素,驗(yàn)證名稱,錯(cuò)誤信息Validator.prototype.add =function (dom, rules) {
? ? ? ? varself =this;
? ? ? ? for(vari = 0, rule; rule = rules[i++];) {
? ? ? ? ? ? (function (rule) {
? ? ? ? ? ? ? ? varary = rule.strategy.split(":");//限制最小數(shù)值,進(jìn)行分割。如沒有:號(hào),則直接返回varerrorMsg = rule.errorMsg;
? ? ? ? ? ? ? ? self.cache.push(function() {//將操作方法封裝到全局?jǐn)?shù)組中varstrategy = ary.shift();//獲取驗(yàn)證方法名稱,并刪除ary.unshift(dom.value);//往開頭添加待驗(yàn)證元素ary.push(errorMsg);//添加驗(yàn)證失敗錯(cuò)誤信息returnstrategies[strategy].apply(dom, ary);//傳遞數(shù)組給方法,因?yàn)椴簧婕皌his,dom也可傳遞null? ? ? ? ? ? ? ? });
? ? ? ? ? ? })(rule)
? ? ? ? }
? ? };
? ? //添加啟動(dòng)方法Validator.prototype.start =function () {
? ? ? ? //將數(shù)組中的方法,分別執(zhí)行。數(shù)組undefined,則跳出循環(huán)for(vari = 0, validatorFunc; validatorFunc =this.cache[i++];) {
? ? ? ? ? ? varmsg = validatorFunc();
? ? ? ? ? ? //又失敗就進(jìn)行跳出if (msg) {
? ? ? ? ? ? ? ? return msg;
? ? ? ? ? ? }
? ? ? ? }
? ? };
? ? //處理校驗(yàn)varvalidatorFunc =function () {
? ? ? ? varvalidator =newValidator();//創(chuàng)建驗(yàn)證邏輯\//添加驗(yàn)證條件? ? ? ? validator.add(registerForm.userName, [
? ? ? ? ? ? {
? ? ? ? ? ? ? ? strategy: 'isNonEmpty',
? ? ? ? ? ? ? ? errorMsg: '用戶不能為空'? ? ? ? ? ? }, {
? ? ? ? ? ? ? ? strategy: 'minLength:2',
? ? ? ? ? ? ? ? errorMsg: '用戶不能少于2位'? ? ? ? ? ? }]);
? ? ? ? validator.add(registerForm.password, [
? ? ? ? ? ? {
? ? ? ? ? ? ? ? strategy: 'minLength:6',
? ? ? ? ? ? ? ? errorMsg: '密碼長(zhǎng)度不能少于6位'? ? ? ? ? ? }]);
? ? ? ? validator.add(registerForm.phoneNumber, [
? ? ? ? ? ? {
? ? ? ? ? ? ? ? strategy: 'isMobile',
? ? ? ? ? ? ? ? errorMsg: '手機(jī)號(hào)碼格式不正確'? ? ? ? ? ? }]);
? ? ? ? varerrorMsg = validator.start();
? ? ? ? return errorMsg;
? ? };
? ? varregisterForm = document.getElementById("registerForm");
? ? registerForm.onsubmit =function () {
? ? ? ? varerrorMsg = validatorFunc();
? ? ? ? if(errorMsg) {//判斷是否有這個(gè)參數(shù)? ? ? ? ? ? alert(errorMsg);
? ? ? ? ? ? returnfalse;
? ? ? ? }
? ? };
})()
果然,有邏輯的js,非常好玩。比CSS好玩多了。感覺有很多委托、多態(tài)的思想。
策略模式的優(yōu)點(diǎn)與缺點(diǎn)
有效的避免許多重復(fù)的復(fù)制粘貼作業(yè)。
開閉原則的完美支持,算法完全獨(dú)立易于切換、理解、拓展。
算法復(fù)用性強(qiáng)
使用組合和委托讓Validator類擁有執(zhí)行算法的能力,也是繼承的一種輕便替代方式
缺點(diǎn)
會(huì)增加許多策略類或策略對(duì)象。
違反迪米特法則,會(huì)將strategy暴露給客戶所有實(shí)現(xiàn)。
代理模式
為一個(gè)對(duì)象提供一個(gè)代用品或占位符,以便控制對(duì)它的訪問。當(dāng)客戶不方便直接訪問一個(gè)對(duì)象的時(shí)候,需要提供一個(gè)替身對(duì)象來控制對(duì)這個(gè)對(duì)象的訪問。
代理模式分為:虛擬代理和保護(hù)代理
虛擬代理:把一些開銷很大的對(duì)象,延遲到真正需要它的時(shí)候才去創(chuàng)建。
保護(hù)代理:用于控制不同權(quán)限的對(duì)象對(duì)目標(biāo)對(duì)象的訪問。
圖片預(yù)加載
使用虛擬代理可以完成圖片預(yù)加載功能,先用一張loading圖片占位,然后用異步方式加載圖片,等圖片加載完畢后填充到img節(jié)點(diǎn)里。
因?yàn)閖avascript事件,均為異步事件。所以當(dāng)執(zhí)行proxyImage時(shí),會(huì)先設(shè)置loading.gif,等圖片加載完畢后,會(huì)執(zhí)行myImage操作。
varmyImage = (function(){
? ? ? varimgNode = document.createElement('img');
? ? ? document.body.appendChild(imgNode);
? ? ? return {
? ? ? ? ? setSrc:function(src){
? ? ? ? ? ? ? imgNode.src = src;
? ? ? ? ? }
? ? ? };
? })();//預(yù)加載方法varproxyImage = (function(){
? ? ? ? varimg =new Image();
? ? ? ? img.onload =function(){
? ? ? ? ? ? myImage.setSrc(this.src);
? ? ? ? }
? ? ? ? return {
? ? ? ? ? ? setSrc:function(src){
? ? ? ? ? ? ? ? myImage.setSrc("loading.gif");
? ? ? ? ? ? ? ? img.src = src;
? ? ? ? ? ? }
? ? ? ? };
? ? })();
? ? proxyImage.setSrc('實(shí)際圖片.jpg');//預(yù)加載myImage.setSrc('實(shí)際圖片'.jpg);//普通加載
注意:加載方法和預(yù)加載方法,必須使用立即執(zhí)行函數(shù),不然setSrc方法調(diào)用不到。
如上預(yù)加載功能,之所以使用代理模式,主要是為了避免違反,單一職責(zé)設(shè)計(jì)原則。
如不使用代理模式,會(huì)執(zhí)行加載圖片和預(yù)加載操作。當(dāng)我們不需要預(yù)加載功能的時(shí)候,無法進(jìn)行快速隔離。
虛擬代理中的惰性加載
將虛擬代理運(yùn)用到惰性加載中,可以讓真實(shí)的代碼延遲到真正實(shí)用的時(shí)候才進(jìn)行添加。
varminiConsole = (function () {
? ? ? ? varcache = [];
? ? ? ? varhandler =function(ev) {//監(jiān)聽按鍵事件if(ev.keyCode === 113) {
? ? ? ? ? ? ? ? varscript = document.createElement('script');
? ? ? ? ? ? ? ? script.onload =function () {
? ? ? ? ? ? ? ? ? ? for(vari = 0, fn; fn = cache[i++];) {
? ? ? ? ? ? ? ? ? ? ? ? fn();
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? };
? ? ? ? ? ? ? ? script.src = 'minConsole.js';
? ? ? ? ? ? ? ? document.getElementsByTagName('head')[0].appendChild(script);
? ? ? ? ? ? ? ? document.body.removeEventListener('keydown', handler);//只加載一次? ? ? ? ? ? }
? ? ? ? };
? ? ? ? document.body.addEventListener('keydown', handler,false);
? ? ? ? return {
? ? ? ? ? ? log: function () {
? ? ? ? ? ? ? ? varargs = arguments;
? ? ? ? ? ? ? ? cache.push(function () {
? ? ? ? ? ? ? ? ? ? return miniConsole.log.apply(miniConsole, args);
? ? ? ? ? ? ? ? });
? ? ? ? ? ? }
? ? ? ? };
? ? })();
? ? miniConsole.log(11);
? ? miniConsole = {
? ? ? ? log: function () {
? ? ? ? ? ? console.log(Array.prototype.join.call(arguments));
? ? ? ? }
? ? };
緩存代理
緩存代理可以為一些開銷大的運(yùn)算結(jié)果提供暫時(shí)的存儲(chǔ),在下次運(yùn)算時(shí),可以使用之前的計(jì)算結(jié)果。
例如使用緩存代理計(jì)算乘積
varmult =function () {
? ? ? ? vara = 1;
? ? ? ? for(vari = 0, l = arguments.length; i < l; i++) {
? ? ? ? ? ? a = a * arguments[i];
? ? ? ? }
? ? ? ? return a;
? ? }
? ? //不使用緩存mult(2,3);varproxyMult = (function () {
? ? ? ? varcache = {};
? ? ? ? returnfunction () {
? ? ? ? ? ? varargs = Array.prototype.join.call(arguments, ',');//把參數(shù)放在一個(gè)字符串里if(argsin cache) {
? ? ? ? ? ? ? ? return cache[args];
? ? ? ? ? ? }
? ? ? ? ? ? returncache[args] = mult.apply(this,arguments);
? ? ? ? };
? ? })();
? ? //使用緩存proxyMult(2,3)
代理工廠?
傳入高階函數(shù)可以為各種計(jì)算方法創(chuàng)建緩存代理。將計(jì)算方法傳入專門用于創(chuàng)建緩存代理的工廠中,這樣就可以創(chuàng)建緩存代理了。
這是使用策略模式,進(jìn)行創(chuàng)建的一種寫法??梢灾苯佣x方法即可。
varstrate = {
? ? ? ? mult: function () {
? ? ? ? ? ? vara = 1;
? ? ? ? ? ? for(vari = 0, l = arguments.length; i < l; i++) {
? ? ? ? ? ? ? ? a = a * arguments[i];
? ? ? ? ? ? }
? ? ? ? ? ? return a;
? ? ? ? },
? ? ? ? plus: function () {
? ? ? ? ? ? vara = 0;
? ? ? ? ? ? for(vari = 0, l = arguments.length; i < l; i++) {
? ? ? ? ? ? ? ? a = a + arguments[i];
? ? ? ? ? ? }
? ? ? ? ? ? return a;
? ? ? ? }
? ? };
? ? varcreateProxyFactory =function (fn) {
? ? ? ? varcache = {};
? ? ? ? returnfunction () {
? ? ? ? ? ? varargs = Array.prototype.join.call(arguments, ',');
? ? ? ? ? ? if(argsin cache) {
? ? ? ? ? ? ? ? return cache[args];
? ? ? ? ? ? }
? ? ? ? ? ? returncache[args] = fn.apply(this, arguments);
? ? ? ? };
? ? };
? ? varproxyMult = createProxyFactory(strate["mult"]);
? ? console.log(proxyMult(1,2,3,4));
觀察者模式
定義對(duì)象間的一種一對(duì)多的依賴關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對(duì)象都將得到通知。
例如事件綁定,就是一個(gè)標(biāo)準(zhǔn)的觀察者模式。
document.body.addEventListener('click',function(){
? ? ? console.log(2);
? ? },false);
? ? document.body.click();
下面進(jìn)行一個(gè)實(shí)際的例子,售樓處可以接受買房登記,登記后的用戶如果有房源,則會(huì)逐一告知。 并且可以進(jìn)行取消登記操作。
為了徹底結(jié)束耦合性,可以使用全局變量制作監(jiān)聽事件。
varObserverEvent = (function () {
? ? ? ? varclientList = [], listen, trigger, remove;
? ? ? ? listen =function (key, fn) {
? ? ? ? ? ? if(!clientList[key]) {
? ? ? ? ? ? ? ? clientList[key] = [];
? ? ? ? ? ? }
? ? ? ? ? ? clientList[key].push(fn);
? ? ? ? };
? ? ? ? trigger =function () {
? ? ? ? ? ? varkey = Array.prototype.shift.call(arguments), fns = clientList[key];
? ? ? ? ? ? if(!fns || fns.length === 0) {
? ? ? ? ? ? ? ? returnfalse;
? ? ? ? ? ? }
? ? ? ? ? ? for(vari = 0, fn; fn = fns[i++];) {
? ? ? ? ? ? ? ? fn.apply(this, arguments);
? ? ? ? ? ? }
? ? ? ? };
? ? ? ? remove =function (key, fn) {
? ? ? ? ? ? varfns = clientList[key];
? ? ? ? ? ? if(!fns) {
? ? ? ? ? ? ? ? returnfalse;
? ? ? ? ? ? }
? ? ? ? ? ? if(!fn) {
? ? ? ? ? ? ? ? fns && (fns.length = 0);
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? for(varl = fns.length - 1; l >= 0; l--) {
? ? ? ? ? ? ? ? ? ? var_fn = fns[l];
? ? ? ? ? ? ? ? ? ? if(_fn === fn) {
? ? ? ? ? ? ? ? ? ? ? ? fns.splice(l, 1);
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? };
? ? ? ? return {
? ? ? ? ? ? listen:listen,
? ? ? ? ? ? trigger:trigger,
? ? ? ? ? ? remove:remove
? ? ? ? }
? ? })();
? ? ObserverEvent.listen('squareMeter88', fn1 =function (price) {
? ? ? ? console.log('價(jià)格=' + price);
? ? });
? ? ObserverEvent.listen('squareMeter100',function (price) {
? ? ? ? console.log('價(jià)格=' + price);
? ? });
? ? ObserverEvent.trigger('squareMeter88', 200000);
? ? ObserverEvent.trigger('squareMeter100', 300000);
? ? ObserverEvent.remove('squareMeter88', fn1);
? ? ObserverEvent.trigger('squareMeter88', 200000);
當(dāng)然這種售樓處只是一個(gè)例子,在現(xiàn)實(shí)中,登錄頁(yè)面登錄后,會(huì)需要刷新各個(gè)模塊的信息(頭像、nav)這類。我們也可以使用觀察者模式進(jìn)行刷新操作。
我們直接改用調(diào)用方法即可,而且是完全的解耦合
varheader = (function () {
? ? ? ? ObserverEvent.listen('loginSucc',function (data) {
? ? ? ? ? ? header.setAvatar(data.avatar);
? ? ? ? });
? ? ? ? return {
? ? ? ? ? ? setAvatar: function (data) {
? ? ? ? ? ? ? ? console.log(data + "設(shè)置header成功");
? ? ? ? ? ? }
? ? ? ? }
? ? })();
? ? varnav = (function () {
? ? ? ? ObserverEvent.listen('loginSucc',function (data) {
? ? ? ? ? ? nav.setAvatar(data.avatar)
? ? ? ? });
? ? ? ? return {
? ? ? ? ? ? setAvatar: function (data) {
? ? ? ? ? ? ? ? console.log(data + '設(shè)置nav成功');
? ? ? ? ? ? }
? ? ? ? }
? ? })();
? ? vardata = {};
? ? data.avatar = "參數(shù)";
? ? ObserverEvent.trigger('loginSucc', data);
觀察者模式的優(yōu)點(diǎn)很明顯:時(shí)間上的解耦,對(duì)象之間的解耦。
命令模式
命令模式指的是一個(gè)執(zhí)行某些特定事情的指令。常見的應(yīng)用場(chǎng)景是:有時(shí)候需要向?qū)ο蟀l(fā)送請(qǐng)求,但不知道接受者是誰,也不知道請(qǐng)求的操作是什么。
例如:訂餐,客人需要給廚師發(fā)送請(qǐng)求,至于那個(gè)廚師做,做的步驟??腿瞬恢?。這就是命令模式。
命令模式的一個(gè)簡(jiǎn)單例子
//定義命令模式執(zhí)行varsetCommand =function (button, func) {
? ? ? ? button.onclick =function () {
? ? ? ? ? ? func.execute();
? ? ? ? };
? ? };
? ? varMenuBar = {
? ? ? ? refresh: function () {
? ? ? ? ? ? console.log("刷新頁(yè)面");
? ? ? ? }
? ? };
? ? varRefreshMenuBarCommand =function (receiver) {
? ? ? ? return {
? ? ? ? ? ? execute: function () {
? ? ? ? ? ? ? ? receiver.refresh();
? ? ? ? ? ? }
? ? ? ? }
? ? };
? ? varrefreshMenuBarCommand = RefreshMenuBarCommand("MenuBar");
? ? setCommand(button1,refreshMenuBarCommand);
命令模式的用處很大,也可以做撤銷命令,回放這種功能。比如,我們把用戶按鍵命令做一個(gè)封裝,制作一個(gè)播放功能。
以下代碼可以執(zhí)行并記錄按鍵,當(dāng)點(diǎn)擊按鈕時(shí),會(huì)執(zhí)行按鍵對(duì)應(yīng)動(dòng)作。
//定義按鍵動(dòng)作varRyu = {
? ? ? ? W: function () {
? ? ? ? ? ? console.log("用戶按下W");
? ? ? ? },
? ? ? ? S: function () {
? ? ? ? ? ? console.log("用戶按下S");
? ? ? ? }
? ? };
? ? //創(chuàng)建命令varmakeCommand =function (receiver, state) {
? ? ? ? returnfunction () {
? ? ? ? ? ? if(receiver[state])
? ? ? ? ? ? ? ? receiver[state]();
? ? ? ? }
? ? };
? ? //可執(zhí)行按鍵Jsonvarcommands = {
? ? ? ? "119": "W",
? ? ? ? "115": "S"? ? };
? ? //保存按鍵記錄varcommandStack = [];
? ? document.onkeypress =function (ev) {
? ? ? ? varkeyCode = ev.keyCode, command = makeCommand(Ryu, commands[keyCode]);
? ? ? ? if (command) {
? ? ? ? ? ? command();
? ? ? ? ? ? commandStack.push(command);
? ? ? ? }
? ? };
? ? //注冊(cè)按鍵監(jiān)聽document.getElementById("replay").addEventListener('click',function () {
? ? ? ? var commad;
? ? ? ? while(command = commandStack.shift()) {
? ? ? ? ? ? command();
? ? ? ? }
? ? }, false);
宏命令
宏命令可以一次執(zhí)行一組命令。我們定義了各種指令,定義了如何執(zhí)行指令。就可以做成一組命令的這種模式了。
針對(duì)不同的步驟,也可以只用此方法。例如:?
var macroCommand2 = new MacroCommand();
macroCommand2.add(macroCommand);
macroCommand2.execute();
可以指定不同命令。
!(function () {
? ? varcloseDoorCommand = {
? ? ? ? execute: function () {
? ? ? ? ? ? console.log("關(guān)門");
? ? ? ? }
? ? };
? ? varopenPcCommand = {
? ? ? ? execute: function () {
? ? ? ? ? ? console.log("開電腦");
? ? ? ? }
? ? };
? ? varopenQQCommand = {
? ? ? ? execute: function () {
? ? ? ? ? ? console.log("登錄QQ");
? ? ? ? }
? ? };
? ? varMacroCommand =function(){
? ? ? return {
? ? ? ? ? commandsList:[],
? ? ? ? ? add:function(command){
? ? ? ? ? ? ? this.commandsList.push(command);
? ? ? ? ? },
? ? ? ? ? execute:function(){
? ? ? ? ? ? ? for(vari= 0,command;command=this.commandsList[i++];){
? ? ? ? ? ? ? ? ? command.execute();
? ? ? ? ? ? ? }
? ? ? ? ? }
? ? ? };
? ? };
? ? varmacroCommand =new MacroCommand();
? ? macroCommand.add(closeDoorCommand);
? ? macroCommand.add(openPcCommand);
? ? macroCommand.execute();
})()
模板方法模式
模板方法是一種只需要繼承就可以實(shí)現(xiàn)的非常簡(jiǎn)單的模式。封裝了子類的算法框架,包含一些公共方法一級(jí)封裝子類中的所有方法執(zhí)行順序。
咖啡與茶??Х鹊牟襟E:1.燒水,2.沖泡,3.倒進(jìn)杯子,4.放牛奶。茶葉的步驟:1.燒水,2.浸泡,3.倒進(jìn)杯子,4.加檸檬
我們分離不同點(diǎn):2,4。然后使用抽象父類定義,并實(shí)現(xiàn)對(duì)應(yīng)方法。其中的init方法,就是模板方法,因?yàn)樗庋b了子類算法框架就,作為一個(gè)算法的模板。
!(function () {
? varBeverage =function(){};
? ? Beverage.prototype.boilWater =function(){//燒水console.log("把水煮沸");
? ? };
? ? Beverage.prototype.brew =function(){};//第二步,方法Beverage.prototype.pourInCup =function(){};
? ? Beverage.prototype.addCondiments=function(){};
? ? Beverage.prototype.init =function(){
? ? ? ? this.boilWater();
? ? ? ? this.brew();
? ? ? ? this.pourInCup();
? ? ? ? this.addCondiments();
? ? };
? ? //創(chuàng)建咖啡子類varCoffee =function(){};
? ? Coffee.prototype =new Beverage();
? ? Coffee.prototype.brew =function(){
? ? ? ? console.log("用沸水沖泡咖啡");
? ? };
? ? Coffee.prototype.pourInCup =function(){
? ? ? ? console.log("把咖啡倒進(jìn)杯子");
? ? };
? ? Coffee.prototype.addCondiments =function(){
? ? ? ? console.log("加糖加牛奶");
? ? };
? ? varCoffee =new Coffee();
? ? Coffee.init();
})()
針對(duì)模板方法,有一些個(gè)性的子類,不打算接受模板約束。那么可以使用鉤子方法來創(chuàng)建。
我們修改模板方法讓他適應(yīng)鉤子方法。
Beverage.prototype.customerWantsCondiments =function () {
? ? ? ? returntrue;
? ? };
? ? Beverage.prototype.init =function () {
? ? ? ? this.boilWater();
? ? ? ? this.brew();
? ? ? ? this.pourInCup();
? ? ? ? if(this.customerWantsCondiments()) {
? ? ? ? ? ? this.addCondiments();
? ? ? ? }
? ? };
? ? //創(chuàng)建咖啡子類varCoffee =function () {
? ? };
? ? Coffee.prototype =new Beverage();
? ? Coffee.prototype.customerWantsCondiments =function(){
? ? ? ? returnwindow.confirm("請(qǐng)問需要調(diào)料嗎");
? ? }
享元模式?
享元模式的核心是運(yùn)用共享技術(shù)來有效支持大量細(xì)粒度的對(duì)象。
例如,現(xiàn)在有50件男裝和50件女裝,分別需要模特穿上并且拍照。如果我們不使用享元模式,那么就需要new 100個(gè)模特。
使用享元模式,只需要new 2個(gè)模特,然后讓他們穿上不同的衣服即可。
?享元模式
享元模式的使用取決于:一個(gè)程序中使用了大量相似對(duì)象。造成很大的內(nèi)存開銷。大多數(shù)狀態(tài)是外部狀態(tài)??梢杂幂^少的功效對(duì)象取代大量對(duì)象。
對(duì)象池?
對(duì)象池維護(hù)一個(gè)裝載空閑對(duì)象的池子,如果需要對(duì)象的時(shí)候,不是直接new,而是轉(zhuǎn)從對(duì)象池里獲取。如果沒有空閑對(duì)象則創(chuàng)建,完成職責(zé)后再次進(jìn)入池子。
我們做一個(gè)公用的對(duì)象池,來維護(hù)新建dom對(duì)象。
!(function () {
? ? varobjectPoolFactory =function (createObjFn) {
? ? ? ? varobjectPool = [];
? ? ? ? return {
? ? ? ? ? ? create: function () {
? ? ? ? ? ? ? ? varobj = objectPool.length === 0 ? createObjFn.apply(this, arguments) : objectPool.shift();
? ? ? ? ? ? ? ? return obj;
? ? ? ? ? ? },
? ? ? ? ? ? recover: function (obj) {
? ? ? ? ? ? ? ? objectPool.push(obj);
? ? ? ? ? ? }
? ? ? ? };
? ? };
? ? variframeFactory = objectPoolFactory(function () {
? ? ? ? variframe = document.createElement("iframe");
? ? ? ? document.body.appendChild(iframe);
? ? ? ? iframe.onload =function () {
? ? ? ? ? ? iframe.onload =null;
? ? ? ? ? ? iframeFactory.recover(iframe);
? ? ? ? }
? ? ? ? return iframe
? ? });
? ? variframe1 = iframeFactory.create();
? ? iframe1.src = "http://www.baidu.com";
? ? setTimeout(function(){
? ? ? ? variframe2 = iframeFactory.create();
? ? ? ? iframe2.src = "http://www.baidu.com";
? ? },2000);
})()
職責(zé)鏈模式
使多個(gè)對(duì)象都有機(jī)會(huì)處理請(qǐng)求,從而避免請(qǐng)求的發(fā)送者和接受者之間的耦合關(guān)系。將對(duì)象形成一條鏈,并沿著這條鏈傳遞請(qǐng)求。
使用orderType和pay來控制流向。分別進(jìn)行不同對(duì)象的流轉(zhuǎn)。
!(function () {
? ? varorder500 =function (orderType, pay, stock) {
? ? ? ? if(orderType === 1 && pay ===true) {
? ? ? ? ? ? console.log("500元定金");
? ? ? ? } else {
? ? ? ? ? ? order200(orderType, pay, stock);
? ? ? ? }
? ? };
? ? varorder200 =function (orderType, pay, stock) {
? ? ? ? if(orderType === 2 && pay ===true) {
? ? ? ? ? ? console.log("200元定金");
? ? ? ? } else {
? ? ? ? ? ? orderNormal(orderType, pay, stock);
? ? ? ? }
? ? };
? ? varorderNormal =function (orderType, pay, stock) {
? ? ? ? if(stock > 0) {
? ? ? ? ? ? console.log("普通購(gòu)買");
? ? ? ? } else {
? ? ? ? ? ? console.log("手機(jī)庫(kù)存不足");
? ? ? ? }
? ? };
? ? order500(1,true,500);
})()
但是這種責(zé)任鏈體系,耦合度比較高,例如500對(duì)象與200對(duì)象,耦合度很高。
!(function () {
? ? varorder500 =function (orderType, pay, stock) {
? ? ? ? if(orderType === 1 && pay ===true) {
? ? ? ? ? ? console.log("500元定金");
? ? ? ? } else {
? ? ? ? ? ? return"nextSuccessor";
? ? ? ? }
? ? };
? ? varorder200 =function (orderType, pay, stock) {
? ? ? ? if(orderType === 2 && pay ===true) {
? ? ? ? ? ? console.log("200元定金");
? ? ? ? } else {
? ? ? ? ? ? return"nextSuccessor";
? ? ? ? }
? ? };
? ? varorderNormal =function (orderType, pay, stock) {
? ? ? ? if(stock > 0) {
? ? ? ? ? ? console.log("普通購(gòu)買");
? ? ? ? } else {
? ? ? ? ? ? console.log("手機(jī)庫(kù)存不足");
? ? ? ? }
? ? };
? ? varChain =function(fn){
? ? ? ? this.fn = fn;
? ? ? ? this.success =null;
? ? };
? ? Chain.prototype.setNextSuccessor =function(successor){
? ? ? ? returnthis.success = successor;
? ? };
? ? Chain.prototype.passRequest =function(){
? ? ? ? varret =this.fn.apply(this,arguments);
? ? ? ? if(ret === "nextSuccessor"){
? ? ? ? ? ? returnthis.success &&this.success.passRequest.apply(this.success,arguments);
? ? ? ? }
? ? };
? ? varchainOrder500 =new Chain(order500);
? ? varchainOrder200 =new Chain(order200);
? ? chainOrder500.setNextSuccessor(chainOrder200);
? ? chainOrder500.passRequest(2,true,200);
})()
中介者模式
中介者模式的作用是解除對(duì)象與對(duì)象之間的緊耦合關(guān)系。增加中介者后,所有的相關(guān)對(duì)象都通過中介者對(duì)象來通信。
泡泡堂例子
1!(function () { 2varplayerDirector = (function () { 3varplayers = {}, operations = {}; 4operations.addPlayer =function (player) { 5varteamColor = player.teamColor; 6players[teamColor] = players[teamColor] || []; 7? ? ? ? ? ? players[teamColor].push(player); 8? ? ? ? }; 9operations.removePlayer =function (player) {10varteamColor = player.teamColor, teamPlayers = players[teamColor] || [];11for(vari = teamPlayers.length - 1; i >= 0; i++) {12if(teamPlayers[i] === player) {13teamPlayers.splice(i, 1);14? ? ? ? ? ? ? ? }15? ? ? ? ? ? }16? ? ? ? };17operations.changeTeam =function (player, newTeamColor) {18? ? ? ? ? ? operations.removePlayer(player);19player.teamColor = newTeamColor;20? ? ? ? ? ? operations.addPlayer(player);21? ? ? ? };22operations.playerDead =function (player) {23varteamColor = player.teamColor, teamPlays = players[teamColor];24varall_dead =true;25for(vari = 0, player; player = teamPlays[i++];) {26if(player.state !== "dead") {27all_dead =false;28break;29? ? ? ? ? ? ? ? }30? ? ? ? ? ? }31if(all_dead ===true) {32for(vari = 0, player; player = teamPlays[i++];) {33? ? ? ? ? ? ? ? ? ? player.lose();34? ? ? ? ? ? ? ? }35for(varcolorin players) {36if(color != teamColor) {37varteamPlayers = players[color];38for(vari = 0, player; player = teamPlayers[i++];) {39? ? ? ? ? ? ? ? ? ? ? ? ? ? player.win();40? ? ? ? ? ? ? ? ? ? ? ? }41? ? ? ? ? ? ? ? ? ? }42? ? ? ? ? ? ? ? }43? ? ? ? ? ? }44? ? ? ? };45varReceiveMessage =function () {46varmessage = Array.prototype.shift.call(arguments);47operations[message].apply(this, arguments);48? ? ? ? };49return {50? ? ? ? ? ? ReceiveMessage:ReceiveMessage51? ? ? ? };52? ? })();53function Player(name, teamColor) {54this.name = name;55this.teamColor = teamColor;56this.state = "alive";57? ? }58Player.prototype.win =function () {59console.log(this.name + " win ");60? ? };61Player.prototype.lose =function () {62console.log(this.name + " lose ");63? ? }64Player.prototype.die =function () {65this.state = "dead";66playerDirector.ReceiveMessage("playerDead",this);67? ? };68Player.prototype.remove =function () {69playerDirector.ReceiveMessage("removePlayer",this);70? ? };71Player.prototype.changeTeam =function (color) {72playerDirector.ReceiveMessage("changeTeam",this, color);73? ? };74varPlayerFacotry =function (name, teamColor) {75varnewPlayer =new Player(name, teamColor);76playerDirector.ReceiveMessage("addPlayer", newPlayer);77return newPlayer;78? ? };7980//測(cè)試81varplayer1 = PlayerFacotry("皮蛋","red"),82player2 = PlayerFacotry("小乖","red");83varplayer3 = PlayerFacotry("黑妞","blue"),84player4 = PlayerFacotry("蔥頭","blue");85? ? player1.die();player2.die();86})()
中介者模式是迎合迪米特法則的一種實(shí)現(xiàn)。指一個(gè)對(duì)象應(yīng)該盡可能的少了解另外的對(duì)象。如果對(duì)象之間的耦合性太高,一個(gè)對(duì)象改變后,會(huì)影響其他對(duì)象。
裝飾者模式
裝飾者模式可以動(dòng)態(tài)的給某個(gè)對(duì)象添加一些額外的職責(zé),而不會(huì)影響從這個(gè)類中派生的其他對(duì)象。
!(function () {
? ? varplance = {
? ? ? ? fire:function(){
? ? ? ? ? ? console.log("發(fā)射普通子彈");
? ? ? ? }
? ? };
? ? varmissileDecorator =function(){
? ? ? ? console.log("發(fā)射導(dǎo)彈");
? ? };
? ? varfire1 = plance.fire;
? ? plance.fire =function(){
? ? ? ? fire1();
? ? ? ? missileDecorator();
? ? };
? ? plance.fire();
})()
或者使用裝飾函數(shù)(AOP)Function的after或者before
!(function () {
? ? varplance =function () { };
? ? plance.prototype.fire =function () {
? ? ? ? console.log("發(fā)射普通子彈");
? ? };
? ? varmissileDecorator =function () {
? ? ? ? console.log("發(fā)射導(dǎo)彈");
? ? };
? ? Function.prototype.after =function (afterfn) {
? ? ? ? var_self =this;
? ? ? ? returnfunction () {
? ? ? ? ? ? varret = _self.apply(this, arguments);
? ? ? ? ? ? afterfn.apply(this, arguments);
? ? ? ? ? ? return ret;
? ? ? ? };
? ? };
? ? varpl =new plance();
? ? pl.fire = pl.fire.after(missileDecorator);
? ? pl.fire();
})()
裝飾函數(shù)是一個(gè)很實(shí)用的功能,例如我們制作插件式的表單驗(yàn)證,就可以使用裝飾函數(shù)。
!(function () {
? ? varregisterForm = document.getElementById("registerForm");
? ? Function.prototype.before =function(beforeFn){
? ? ? ? var_self =this;
? ? ? ? returnfunction(){
? ? ? ? ? ? if(beforeFn.apply(this,arguments) ===false){
? ? ? ? ? ? ? ? return;
? ? ? ? ? ? }
? ? ? ? ? ? return_self.apply(this,arguments);
? ? ? ? };
? ? }
? ? varvalidata =function () {
? ? ? ? if(registerForm.userName.value === ""){
? ? ? ? ? ? alert("用戶名不能為空");
? ? ? ? ? ? returnfalse;
? ? ? ? }
? ? };
? ? varformSubmit =function(){
? ? ? ? console.log("成功");
? ? }
? ? formSubmit = formSubmit.before(validata);
? ? registerForm.onsubmit =function(){
? ? ? ? formSubmit();
? ? ? ? returnfalse;
? ? };
})()
代理模式OR裝飾器模式
代理模式和裝飾器模式相似的地方很多,都是有單獨(dú)的一個(gè)對(duì)象提供間接訪問。他們最大的不同就是設(shè)計(jì)的意圖和目的。
代理模式的目的是:當(dāng)直接訪問本體不方便或者不符合需求時(shí),為這個(gè)本體提供一個(gè)替代者。
裝飾器模式的目的是:為對(duì)象動(dòng)態(tài)加入一些行為。
例如圖片預(yù)加載,代理提供預(yù)加載功能是調(diào)用原來的方法也就是跟本體做的事情一樣,而裝飾器模式則是添加新的職責(zé)和行為。
狀態(tài)模式
狀態(tài)模式關(guān)鍵是區(qū)分事物內(nèi)部的狀態(tài),事物內(nèi)部狀態(tài)改變會(huì)帶來事物行為的改變。
第一個(gè)例子:電燈開關(guān),同樣是按下開關(guān),電燈亮或者不亮,表達(dá)的行為是不一樣的。buttonWasPressed 方法是變化的點(diǎn)。
!(function () {
? ? varLightEvent = {
? ? ? ? on:function(){
? ? ? ? ? ? console.log("關(guān)燈");
? ? ? ? ? ? this.state = "off";
? ? ? ? },
? ? ? ? off:function(){
? ? ? ? ? ? console.log("開燈");
? ? ? ? ? ? this.state = "on";
? ? ? ? }
? ? };
? ? varLight =function () {
? ? ? ? this.state = "off";
? ? ? ? this.button =null;
? ? };
? ? Light.prototype.init =function(){
? ? ? ? varbutton = document.createElement("button"),self=this;
? ? ? ? button.innerHTML = "開關(guān)";
? ? ? ? this.button = document.body.appendChild(button);
? ? ? ? this.button.onclick =function(){
? ? ? ? ? ? self.buttonWasPressed();
? ? ? ? };
? ? };
? ? Light.prototype.buttonWasPressed =function(){
? ? ? ? LightEvent[this.state].apply(this,arguments);
? ? };
? ? varlight =new Light();
? ? light.init();
})()
上一個(gè)例子使用策略模式來完成的,我們來說一下策略模式與狀態(tài)模式的區(qū)別
策略模式中各個(gè)策略類是平等又平行的,他們之間沒有任何聯(lián)系。狀態(tài)類的行為早已被封裝好了,改變行為發(fā)生在狀態(tài)模式內(nèi)部。
那么我是使用狀態(tài)模式,來完成上面電燈的例子。
!(function () {
? ? vardelegate =function(client,delegation){
? ? ? ? return {
? ? ? ? ? ? buttonWasPressed:function(){
? ? ? ? ? ? ? ? return delegation.buttonWasPressed.apply(client,arguments);
? ? ? ? ? ? }
? ? ? ? };
? ? };
? ? varFSM = {
? ? ? ? off: {
? ? ? ? ? ? buttonWasPressed: function () {
? ? ? ? ? ? ? ? console.log("關(guān)燈");
? ? ? ? ? ? ? ? this.currState =this.onState;
? ? ? ? ? ? }
? ? ? ? },
? ? ? ? on: {
? ? ? ? ? ? buttonWasPressed: function () {
? ? ? ? ? ? ? ? console.log("開燈");
? ? ? ? ? ? ? ? this.currState =this.offState;
? ? ? ? ? ? }
? ? ? ? }
? ? };
? ? varLight =function () {
? ? ? ? this.offState = delegate(this,FSM.off);
? ? ? ? this.onState = delegate(this,FSM.on);
? ? ? ? this.currState = FSM.off;
? ? ? ? this.button =null;
? ? };
? ? Light.prototype.init =function () {
? ? ? ? varbutton = document.createElement("button"), self =this;
? ? ? ? button.innerHTML = "開關(guān)";
? ? ? ? this.button = document.body.appendChild(button);
? ? ? ? this.button.onclick =function () {
? ? ? ? ? ? self.currState.buttonWasPressed.call(self);
? ? ? ? };
? ? };
? ? varlight =new Light();
? ? light.init();
})()