单例模式 惰性单例模式封装
1 2 3 4 5 6 var getSingle = function (fn ) { var result; return function ( ) { return result || ( result = fn.apply(this , arguments ) ); } }
策略模式
定义:定义一系列的算法,把他们一个个封装起来,并使他们可以相互替换。 除了算法外,策略模式还可以用来封装一系列的业务规则,例如:表单校验,业务规则可以对应表单校验的规则和错误的提示语。定义好规则策略后,需要校验的地方传入规则名和错误提示即可。
代理模式
定义:代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问。
保护代理:可以增加条件做过滤,拒绝掉一部分的请求,控制不同权限的对象对目标对象的访问 虚拟代理:对耗时或者可合并的操作等,做延迟处理,在合适的时机触发。例如:加载图片、合并接口请求等。
合并请求的虚拟代理例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 var synchromousFile = function ( id ) { console .log('开始同步文件,id为' + id); }; var proxySynchronousFile = (function ( ) { var cache = [], timer; return function ( id ) { cache.push(id); if ( timer ) { return ; } timer = setTimeout (function ( ) { synchromousFile( cache.join(',' )); clearTimeout ( timer ); timer = null ; cache.length = 0 ; }, 2000 ) } })();
发布-订阅模式
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都将得到通知。 优点:一为时间上的解耦,二为对象之间的解耦。 缺点:过多的使用会使得模块之间的联系被隐藏到了背后。
以下为全局的发布-订阅对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 var Event = (function ( ) { var clientList = {}, listen, trigger, remove; listen = function (key, fn ) { if (!clientList[ key ]) { clientList[ key ] = []; } clientList[ key ].push( fn ); }; trigger = function ( ) { var key = Array .prototype.shift.call( arguments ), fns = clientList[ key ]; if ( !fns || fns.length === 0 ) { return false ; } for ( var i = 0 , fn; fn = fns[ i++ ]; ) { fn.apply( this , arguments ); } } remove = function ( key, fn ) { var fns = clientList[ key ]; if ( !fns ) { return false ; } if ( !fn ) { fns && ( fns.length = 0 ); } else { for ( var l = fns.length - 1 ; l >= 0 ; l--) { var _fn = fns[ l ]; if ( _fn === fn ) { fns.splice( l, 1 ); } } } }; return { listen : listen, trigger : trigger, remove : remove } })();
命令模式
定义:指的是一个执行某些特定事情的指令。 最常用的场景:有时候需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是什么。此时希望用一种松耦合的方式来设计程序,使得请求发送者和请求接收者能够消除彼此之间的耦合关系。 命令模式支持撤销、排队、宏命令等。
使用闭包的命令模式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 var setCommand = function ( button, func ) { button.onclick = function ( ) { func(); } }; var MenuBar = { refresh : function ( ) { console .log( '刷新' ); } }; var RefreshMenuBarCommand = function ( receiver ) { return function ( ) { receiver.refresh(); } }; var refreshMenuBarCommand = RefreshMenuBarCommand( MenuBar );setCommand( button1, refreshMenuBarCommand);
组合模式 组合模式将对象组合成树形结构,以表示“部分-整体”的层次结构。除了用来表示树形结构之外,组合模式的另一个好处是通过对象的多态性表现,使得用户对单个对象和组合对象的使用具有一致性,下面分别说明。
表示树形结构。通过回顾上面的例子,我们很容易找到组合模式的一个优点:提供了一种遍历树形结构的方案,通过调用组合对象的execute方法,程序会递归调用组合对象下面的叶对象的execute方法,所以我们的万能遥控器只需要一次操作,便能依次完成关 门、打开电脑、登录QQ这几件事情。组合模式可以非常方便地描述对象部分-整体层次结构。
利用对象多态性统一对待组合对象和单个对象。利用对象的多态性表现,可以使客户端忽略组合对象和单个对象的不同。在组合模式中,客户将统一地使用组合结构中的所有对象,而不需要关心它究竟是组合对象还是单个对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 var MacroCommand = function ( ) { return { commandsList : [], add : function ( command ) { this .commandsList.push( command ); }, execute : function ( ) { for ( var i = 0 , command; command = this .commandsList[ i++ ]; ){ command.execute(); } } } }; <!--打开空调命令--> var openAcCommand = { execute : function ( ) { console .log( '打开空调' ); } }; <!--打开电视和音响--> var openTvCommand = { execute : function ( ) { console .log( '打开电视' ); } }; var openSoundCommand = { execute : function ( ) { console .log( '打开音响' ); } }; var macroCommand1 = MacroCommand();macroCommand1.add(openTvCommand) macroCommand1.add(openSoundCommand) <!--关门、打开电脑和打登录QQ的命令--> var closeDoorCommand = { execute : function ( ) { console .log( '关门' ); } }; var openPcCommand = { execute : function ( ) { console .log( '开电脑' ); } }; var openQQCommand = { execute : function ( ) { console .log( '登录QQ' ); } }; var macroCommand2 = MacroCommand();macroCommand2.add( closeDoorCommand ); macroCommand2.add( openPcCommand ); macroCommand2.add( openQQCommand ); <!--把各宏命令装进一个超级命令中去--> var macroCommand = MacroCommand();macroCommand.add( openAcCommand ); macroCommand.add( macroCommand1 ); macroCommand.add( macroCommand2 );
享元模式
享元模式的核心是运用共享技术来有效支持大量细粒度的对象,如果系统中因为创建了大量类似的对象而导致内存占用过高,享元模式就非常有用。
享元模式要求对象的属性划分为内部状态和外部状态,如何划分:
内部状态存储于对象内部
内部状态可以被一些对象共享
内部状态独立于具体的场景,通常不会变化
外部状态取决于具体的场景,并根据场景而变化,外部状态不能被共享。
示例:文件上传
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 var Upload = function (uploadType ) { this .uploadType = uploadType; } Upload.prototype.delFile = function (id ) { uploadManger.setExternalState(id, this ); if (this .fileSize < 3000 ) { return this .dom.parentNode.removeChild(this .dom); } if (window .confirm("确定要删除文件吗?" + this .fileName)) { return this .dom.parentNode.removeChild(this .dom); } } var UploadFactory = (function ( ) { var createdFlyWeightObjs = {}; return { create : function (uploadType ) { if (createdFlyWeightObjs[ uploadType ]) { return createdFlyWeightObjs[ uploadType ] } return createdFlyWeightObjs[ uploadType ] = new Upload( uploadType ); } } })(); var uploadManger = (function ( ) { var uploadDatabase = {}; return { add : function (id, uploadType, fileName, fileSize ) { var flyWeightObj = UploadFactory.create(uploadType); var dom = document .createElement('div' ); dom.innerHTML = "<span>文件名称:" + fileName + ",文件大小:" + fileSize +"</span>" + "<button class='delFile'>删除</button>" ; dom.querySelector(".delFile" ).onclick = function ( ) { flyWeightObj.delFile(id); }; document .body.appendChild(dom); uploadDatabase[id] = { fileName : fileName, fileSize : fileSize, dom : dom }; return flyWeightObj; }, setExternalState : function (id, flyWeightObj ) { var uploadData = uploadDatabase[id]; for (var i in uploadData) { flyWeightObj[i] = uploadData[i]; } } }; })(); var id = 0 ;window .startUpload = function (uploadType, files ) { for (var i=0 ,file; file = files[i++];) { var uploadObj = uploadManger.add(++id, uploadType, file.fileName, file.fileSize); } }; startUpload("plugin" , [ { fileName : '1.txt' , fileSize : 1000 },{ fileName : '2.txt' , fileSize : 3000 },{ fileName : '3.txt' , fileSize : 5000 } ]); startUpload("flash" , [ { fileName : '4.txt' , fileSize : 1000 },{ fileName : '5.txt' , fileSize : 3000 },{ fileName : '6.txt' , fileSize : 5000 } ]);
职责链模式
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
例子:异步职责链的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 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 === 'nextSuccessor' ) { return this .successor && this .successor.passRequest.apply( this .successor, arguments ); } return ret; } Chain.prototype.next = function ( ) { return this .nextSuccessor && this .successor.passRequest.apply( this .successor, arguments ); } var fn1 = new Chain(function ( ) { console .log( 1 ); return 'nextSuccessor' ; }); var fn2 = new Chain(function ( ) { console .log( 2 ); var self = this ; setTimeout (function ( ) { self.next(); }, 1000 ); }); var fn3 = new Chain(function ( ) { console .log( 3 ); }); fn1.setNextSuccessor( fn2 ).setNextSuccessor( fn3 ); fn1.passRequest();
用AOP实现职责链
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Function .prototype.after = function ( fn ) { var self = this ; return function ( ) { var ret = self.apply( this , arguments ); if ( ret === 'nextSuccessor' ){ return fn.apply( this , arguments ); } return ret; } }; var order = order500yuan.after( order200yuan ).after( orderNormal );order( 1 , true , 500 ); order( 2 , true , 500 ); order( 1 , false , 500 );
中介者模式
中介者模式的作用就是解除对象与对象之间的紧耦合关系。增加一个中介者对象后,所有的相关对象都通过中介者对象来通信,而不是互相引用,所以当一个对象发生改变时,只需要通知中介者对象即可。中介者使各对象之间耦合松散,而且可以独立地改变它们之间的交互。
例子:游戏中使用中介者控制游戏状态
玩家对象的生成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 function Player (name, teamColor ) { this .name = name; this .teamColor = teamColor; this .state = 'alive' ; }; Player.prototype.win = function ( ) { console .log(`${this .name} won!` ); }; Player.prototype.lose = function ( ) { console .log(`${this .name} lost!` ); }; Player.prototype.die = function ( ) { this .state = 'dead' ; playerDirector.ReceiveMessage( 'playDead' , this ); } Player.prototype.remove = function ( ) { playerDirector.ReceiveMessage( 'removePlayer' , this ); } Player.prototype.remove = function ( color ) { playerDirector.ReceiveMessage( 'changeTeam' , this , color ); } var palyerFactory = function ( name, teamColor ) { var newPlayer = new Player( name, teamColor ); playerDirecotor.ReceiveMessage( 'addPlayer' , newPlayer ); return newPlayer; }
中介者playerDirector的实现:
利用发布—订阅模式。将playerDirector实现为订阅者,各player作为发布者,一旦player的状态发生改变,便推送消息给playerDirector,playerDirector处理消息后将反馈发送给其他player。
在playerDirector中开放一些接收消息的接口,各player可以直接调用该接口来给playerDirector发送消息,player只需传递一个参数给playerDirector,这个参数的目的是使playerDirector可以识别发送者。同样,playerDirector接收到消息之后会将处理结果反馈给其他player。
以下采取开发接口的方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 var playerDirector = ( function ( ) { var players = {}, operations = {}; operations.addPlayer = function ( player ) { var teamColor = player.teanmColor; players[ teamColor ] = players[ teamColor ] || []; players[ teamColor ].push( player ); } operations.removePlayer = function ( player ) { var teamColor = player.teamColor, teamPlayers = players[ teamColor ] || [] for ( var i = teamPlayers.length - 1 ; i >= 0 ; i-- ){ if ( teamPlayers[ i ] === player ){ teamPlayers.splice( i, 1 ); } } } operations.changeTeam = function ( player, newTeamColor ) { operations.removePlayer( player ); player.teamColor = newTeamColor; operations.addPlayer( player ); } operations.playerDead = function ( player ) { var teamColor = player.teamColor, teamPlayers = players[ teamColor ]; var all_dead = true ; for ( var i = 0 , player; player = teamPlayers[ i++ ]; ){ if ( player.state !== 'dead' ){ all_dead = false ; break ; } } if ( all_dead === true ){ for ( var i = 0 , player; player = teamPlayers[ i++ ]; ){ player.lose(); } for ( var color in players ){ if ( color !== teamColor ){ var teamPlayers = players[ color ]; for ( var i = 0 , player; player = teamPlayers[ i++ ]; ){ player.win(); } } } } }; var ReceiveMessage = function ( ) { var message = Array .prototype.shift.call( arguments ); operations[ message ].apply( this , arguments ); } return { ReceiveMessage : ReceiveMessage } })();
装饰者模式
装饰者模式能够在不改变对象自身的基础上,在程序运行期间给对象动态的添加职责。
一个简单的例子:
1 2 3 4 5 6 7 8 9 10 11 12 var a = function ( ) { alert (1 ); } var _a = a;a = function ( ) { _a(); alert (2 ); } a();
用AOP装饰函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 Function .prototype.before = function ( beforeFn ) { var _self = this ; return function ( ) { beforeFn.apply( this , arguments ); return _self.apply( this , arguments ); } } Function .prototype.after = function ( afterfn ) { var _self = this ; return function ( ) { var ret = _self.apply( this , arguments ); afterfn.apply( this , arguments ); return ret; } } window .onload = function ( ) { alert (1 ); } window .onload = ( window .onload || function ( ) {} ).after(function ( ) { alert (2 ); }).after(function ( ) { alert (3 ); }).after(function ( ) { alert (4 ); });
装饰者模式和代理模式
代理模式和装饰者模式最重要的区别在于它们的意图和设计目的。代理模式的目的是,当直接访问本体不方便或者不符合需要时,为这个本体提供一个替代者。本体定义了关键功能,而代理提供或拒绝对它的访问,或者在访问本体之前做一些额外的事情。装饰者模式的作用就是为对象动态加入行为。换句话说,代理模式强调一种关系(Proxy与它的实体之间的关系),这种关系可以静态的表达,也就是说,这种关系在一开始就可以被确定。而装饰者模式用于一开始不能确定对象的全部功能时。代理模式通常只有一层代理-本体的引用,而装饰者模式经常会形成一条长长的装饰链。
状态模式
定义:允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。 我们以逗号分割,把这句话分为两部分来看。第一部分的意思是将状态封装成独立的类,并将请求委托给当前的状态对象,当对象的内部状态改变时,会带来不同的行为变化。 第二部分是从客户的角度来看,我们使用的对象,在不同的状态下具有截然不同的行为,这个对象看起来是从不同的类中实例化而来的,实际上这是使用了委托的效果。
状态模式的通用结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 var Light = function ( ) { this .offLightState = new OffLightState( this ); 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 = '开关' ; this .currState = this .offLightState; this .button.onclick = function ( ) { self.currState.buttonWasPressed(); } } var OfflightState = function ( light ) { this .light = light; } OfflightState.prototypr.buttonWasPressed = function ( ) { console .log('弱光' ); this .light.setState( this .light.weakLightState ); }
状态机:使用闭包实现将客户的操作委托给状态对象,此委托需要处理this的劫持
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 var delegate = function ( client, delegation ) { return { buttonWasPressed : function ( ) { return delegation.buttonWasPressed.apply( client, arguments ); } } }; var FSM = { off : { buttonWasPressed : function ( ) { console .log( '关灯' ); this .button.innerHTML = '下一次按我是开灯' ; this .currState = this .onState; } }, on : { buttonWasPressed : function ( ) { console .log( '开灯' ); this .button.innerHTML = '下一次按我是关灯' ; this .currState = this .offState; } } }; var Light = function ( ) { this .offState = delegation( this , FSM.off ); this .onState = delegation( this , FSM.on ); this .currState = this .offState; this .button = null ; } Light.prototype.init = function ( ) { var button = document .createElement( 'button' ); button.innerHTML = '已关灯' ; this .button = document .body.appendChild( button ); this .button.onclick = function ( ) { self.currState.buttonWasPressed(); } } var light = new Light();light.init();
适配器模式
适配器模式的作用是解决两个软件实体间的接口不兼容的问题。使用适配器模式之后,原本由于接口不兼容而不能工作的两个软件实体可以一起工作。 实际项目中封装组件时,经常遇到组件定义的接口和后台返回的数据无法完全对应上,这时一个apater函数转化数据是比较方便的选择。