咱们先来看看java里设计模式有哪些前端
好了,咱们就用这张图做为引子,开撸了!!!go! go !go!vue
这里咱们只关心两大原则(是js经常使用的两大原则,单一职责原则和开关闭合原则,既然入门,内容就越简单越好)java
单一职责原则很简单,一个方法 一个类只负责一个职责,各个职责的程序改动,不影响其它程序。node
// 我的认为组件化思想就是单一职责原则很好的体现
// 组件的好处在于若是咱们有10个页面都须要这个组件,那么咱们就能够重用这个组件,复用代码
// 并且当这个组件的逻辑须要改变的时候,通常状况下咱们只须要改这个组件自己的逻辑就行了,不影响其它组件
复制代码
如类、模块和函数,应当对扩展开放,但对修改关闭mysql
// 举个例子,我最深的体会就是固定逻辑抽象出来,而后不固定的逻辑方便拓展 // 这里咱们能够看到,getUserInfo就是获取用户信息的逻辑是不变的,咱们就封装到一个方法里,这就是固定逻辑 const getUserInfo = function( callback ){ $.ajax('http:// xxx.com/getUserInfo', callback) } // 可是用获取的信息作的事情可能不一样,是变化的,这就是不固定的逻辑 getUserInfo((data)=>{ console.log(data.userName) }) getUserInfo((data)=>{ console.log(data.userId) }) 复制代码
在我看来建立型设计模式就是如何建立对象的设计模式,好比咱们常见的建立对象以下react
const obj = new Object()
复制代码
但实际写代码的过程当中,建立对象的复杂度比上面的代码高不少。接下来介绍一种常见的建立对象的设计模式叫简单工厂模式webpack
这个模式我我的以为核心意义在于只暴露出一个工厂方法,实际上建造什么样的实例对象(也就是new 哪个构造函数)咱们不用关心。(我在react15版本源码里看到createElement()方法就是用的这种模式)es6
// 暴露出一个工厂类,或者你也写成构造函数也行,好比说这个工厂类叫User,构造不同的角色,不一样的角色的有不同的属性 class User { //构造器 constructor(opt) { this.viewPage = opt.viewPage; this.name = opt.name; } //静态方法,这是内部实现工厂方法的细节 static getInstance(role) { switch (role) { case 'superAdmin': return new User({ name: '超级管理员', viewPage: ['首页', '通信录', '发现页', '应用数据', '权限管理'] }); break; case 'admin': return new User({ name: '管理员', viewPage: ['首页', '通信录', '发现页', '应用数据'] }); break; case 'user': return new User({ name: '普通用户', viewPage: ['首页', '通信录', '发现页'] }); break; default: throw new Error('参数错误, 可选参数:superAdmin、admin、user') } } } //调用 let superAdmin = User.getInstance('superAdmin'); let admin = User.getInstance('admin'); let normalUser = User.getInstance('user'); 复制代码
保证一个类仅有一个实例。咱们简单看一下代码就明白什么意思了,单例模式仍是比较好理解。web
// 这是单例类 var Singleton = function( name ){ this.name = name; }; // 暴露出一个静态方法,来获取单例 Singleton.getInstance = (function(){ var instance = null; return function( name ){ // 第一次执行getInstance函数,由于变量instance是null,因此执行if里面的语句 // 第二次执行getInstance函数,由于变量instance是new Singleton( name ),因此执行不执行if里的语句 if ( !instance ){ instance = new Singleton( name ); } return instance; } })(); 复制代码
好啦,建立型设计模式咱们就介绍这两种,是否是比唱跳rap和篮球要轻松一些呢,接下来介绍结构型设计模式ajax
结构型模式关注于总体最终的结构,经过继承和组合,构建出更加复杂的结构。就拿下面第一个适配器模式,咱们很快的理解一下什么叫出更加复杂的结构
是指将一个接口转换成本身但愿的另一个接口。
// 好比获取到后端传来的数据,但这个数据不是咱们想要的格式 // 这个时候就能够用适配器来转换一下 function ajaxAdapter(data){ // 处理数据并返回新数据 // 经过对象解构,获取list列表,data数据假如是 {code: 200, data: {list: [{name:1}]} } const { data: { list } } = data return list } $.ajax({ url: 'http://zxx', success(data){ doSomething(ajaxAdapter(data)) } }) 复制代码
是指在不改变原对象的基础上,经过对其进行包装拓展(添加属性或者方法)使原有对象能够知足用户更复杂的需求
// 咱们声明了一个对象小蔡,他只会打篮球 let xiaoCai={ sing(){ console.log("你们好,我是小蔡,我会打篮球"); } }; // 咱们声明了一个对象小李,他也想学小蔡打篮球 let xiaoLi={ sing(){ console.log("你们好,我是小李,我也想学打篮球"); } }; // 小李在B站看了视频以后,也会打篮球了 // 把小李的sing方法保存起来 const xiaoLiSing = xiaoLi.sing // 重写小李的sing方法,把小蔡的篮球技术给小李 xiaoLi.sing = () => { xiaoLiSing() xiaoCai.sing() } 复制代码
为一组复杂的子系统接口提供一个更高级的统一接口,经过这个接口使得对子系统系统接口的访问更容易。
// js里好比根据不一样浏览器作兼容处理,提供统一接口 // 有的浏览器经过DOM元素的innerText属性获取其中的值,好比<div>hello</div>中hello这个文本 // 有的浏览器经过DOM元素的textContent属性获取其中的值 function getInnerText(element){ //判断element是否支持innerText if(typeof element.innerText === 'string'){ return element.innerText; }else{ return element.textContent; } } 复制代码
是指
// 举例场景是缓存代理 // 缓存是指每次传入的参数和结果缓存到一个对象里 // 这样下一次传入一样的参数,若是以前在缓存对象里,就直接拿缓存里的数据 // 这样就不用每次都要调用函数去计算,有缓存直接用缓存,提高了效率 var plus = function(...argArray){ var a = 0; for(var i = 0; i < argArray.length; i++){ a = a + argArray[i] } return a } // 高阶函数(将函数做为参数或者返回值的函数)包装一下上面的plus函数 var proxyFactory = function(fn) { var cache = {}; // 参数缓存列表 return function(...argArray){ // 将传入的参数变为字符串,做为 var argString = argArray.join(); // 若是在缓存里就输出缓存内容 var argsString = String(argArray) if(argsString in cache){ return cache[argsString] } // 若是没有在缓存里就保存在缓存中 return cache[argsString] = fn(...argArray) } } // 测试 const proxyPlus = proxyFactory(plus) console.log(proxyPlus(5,6,7,8)) 复制代码
// 这个例子就是抽象层和实现层的解耦 // 提取共同点(抽象层)给每一个对象都添加公共方法,即addMethod方法 Object.prototype.addMethod = function(name,fn){ this[name] = fn; } // 建立类并实例化对象(实现层) function Box(x,y,z){ this.x=x; this.y=y; this.z=z; } var box=new Box(20,10,10); // 为对象拓展方法(桥接方法) box.addMethod("init",function(){ console.log("盒子的长度为:"+this.x+" , 宽度为:"+this.y+" , 高度为:"+this.z); }); // 测试代码 box.init(); 复制代码
// 这里的场景是加入有一堆命令,经过组合模式构建更复杂的,自定义的命令集合 // 宏命令的代码 var closeDoorCommand = {//做为叶对象 execute: function(){ console.log( '关门' ); } }; var openPcCommand = { //做为叶对象 execute: function(){ console.log( '开电脑' ); } }; var openQQCommand = {//做为叶对象 execute: function(){ console.log( '登陆QQ' ); } }; //组合模式的根对象 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(); //叶对象都有execute方法 } } } }; var macroCommand = MacroCommand(); macroCommand.add( closeDoorCommand );//添加到根对象数组中 macroCommand.add( openPcCommand );//同上 macroCommand.add( openQQCommand );//同上 macroCommand.execute();//执行根命令 复制代码
// 举例对象池子 // 对象池是另一种性能优化方案,和享元模式有一些类似之处,但没有分离内部状态和外部状态这个过程 // 创建一个对象池工厂 objectPoolFactory var objectPoolFactory = function (createObjFn) { var objectPool = []; //对象池 return { create: function () { //取出 var obj = objectPool.length === 0 ? createObjFn.apply(this,arguments) : objectPool.shift(); return obj; }, recover: function (obj) { //收回 objectPool.push(obj); } } }; // 如今利用objectPoolFactory来建立一个装载一些iframe的对象池 var iframeFactory = objectPoolFactory(function () { var iframe = document.createElement('iframe'); document.body.appendChild(iframe); iframe.onload = function () { iframe.onload = null; //防止iframe重复加载的bug iframeFactory.recover(iframe); //iframe加载完成后往对象池填回节点(收回) }; return iframe; }); //调用 var iframe1 = iframeFactory.create(); iframe1.src = 'http://www.qq.com'; 复制代码
建立型设计模式解决了如何建立对象,那建立对象后能作什么呢,接着结构型模式描述如何将类和对象组合起来,造成更大的结构。
最后就是行为型设计模式闪亮登场了,行为型设计模式不只仅关注类和对象的结构,并且重点关注它们之间的相互做用。
它定义一个操做中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类能够不改变一个算法的结构便可重定义该算法的某些特定步骤,具体实现demo以下
// 模板方法分为两部分:一、抽象的父类 二、具体实现的子类 // 父类,咱们这里举例称做饮料类,即Beverage class Beverage{ // 冲饮料第一步须要把水煮沸 boilWater(){ console.log('把水煮沸') } // 冲饮料第二部须要把材料放入沸水 brew(){ } // 冲饮料第三步把饮料倒进杯子里 addCoundiments(){ } init(){ this.boilWater(); this.brew(); this.addCondiments(); } } // 子类,咱们假如冲茶 class Tea extends Beverage{ // 冲饮料第二步须要把材料放入沸水 brew(){ console.log('用沸水泡茶') } // 冲饮料第三步把饮料倒进杯子里 addCondiments(){ console.log('把茶放入杯子里') } } const tea = new Tea() tea.init() 复制代码
将请求与实现解耦并封装成独立对象,从而使不一样的请求对客户端的实现参数化
// 好比二次封装react的antd组件,我几乎都是用命令模式去封装的 // 在个人运用中,这个设计模式就是用数据去驱动UI,好比用的elementUI\antd组件,是否是放入数据,视图就自动生成了呢 // 这里简单将命令模式应用于canvas绘图 const CanvasCommand = (function(){ const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); const Action = { // 填充颜色 fillStyle(c){ ctx.fillStyle = c } // 填充矩形 fillRect(x, y, width, height){ ctx.fillRect(x, y, width, height) } } return { execute(msg){ if(msg.length){ msg.forEach((v)=>{ Action[v.command].apply(Action, msg.param) } ) } else { return } } } } })() // 写命令 CanvasCommand.excute([ {command: 'fillStyle', param: 'red'}, {command: 'fillRect', param: [20, 20, 100, 100]} ]) 复制代码
// 手写一个基于发布订阅模式的事件绑定机制 const event = { // 事件都注册在eventList数组里 eventList: [], /** * 订阅事件函数 * @key {string} 订阅事件名称 * @fn {function} 订阅事件的回调函数 **/ listen(key, fn) { if(typeof fn !== 'function'){ console.log('请添加回调函数'); return } if(!this.eventList[key]) { this.eventList[key] = [] } this.eventList[key].push(fn) }, trigger(key, ...arg){ const fns = this.eventList[key]; if(!fns || fns.length === 0){ return false } fns.forEach((fn) => { fn.apply(this. arg) }) } } // 测试 event.listen('click', function(){ console.log('订阅事件1') }) event.listen('click', function(){ console.log('订阅事件2') }) event.trigger('click') 复制代码
// 常规写法 function showResult(level) { if(level === 'A'){ return '10颗星' }else if(level === 'B'){ return '9颗星' }else if(level === 'C'){ return '8颗星' }else if(level === 'D'){ return '7颗星' }else if(level === 'E'){ return '6颗星' }else if(level === 'F'){ return '5颗星' } } console.log(showResult('F')) 复制代码
假如if...else有1000种可能,那么若是这么判断下去,要判断1000次才能匹配if...else的最后一个条件,效率就特别低,如今咱们用策略模式去改进
// 策略模式改进,把if的条件定义为一个个的算法 const state = { A() { return '10颗星' }, B() { return '9颗星' }, C() { return '8颗星' }, D() { return '7颗星' }, E() { return '6颗星' }, F() { return '5颗星' }, } // 策略模式调用 function showResult(level){ return state[level]() } console.log(showResult('F')) 复制代码
// 写一个JS版的泡泡堂游戏, 这是本身在书上看到的一个很好的中介者模式案例 // 目前玩家是两我的,因此当其中一个玩家死亡的时候游戏便结束,同时通知它的对手胜利 function Player( name ) { this.name = name; this.enemy = null; } Player.prototype.win = function(){ console.log(this.name + 'won') } Player.prototype.lose = function(){ console.log(this.name + 'lost') } Player.prototype.die = function(){ this.lose(); this.enemy.win(); } // 接下来建立2个玩家对象 var player1 = new Player('沈腾'); var player2 = new Player('贾玲'); // 给玩家相互设置敌人 player1.enemy = player2; player2.enemy = player1; // 当玩家player1被泡泡炸死的时候,只须要调用这一句代码 便完成了一局游戏 player1.die() // 接下来,假如玩家数量变为了8个,咱们的游戏代码就变成了下面这样 // 如下建立人物的代码这里不须要看懂,接着日后面看就好了,我是怕有些人跑代码跑不通,因此现把队伍的人创建起来 // 定一个数组players来保存全部玩家 var players = [] // 红队 var player1 = playerFactory('沈腾', 'read'); var player2 = playerFactory('黄海波', 'read'); var player3 = playerFactory('张全蛋', 'read'); var player4 = playerFactory('李大嘴', 'read'); // 蓝队 var player5 = playerFactory('小沈阳', 'blue'); var player6 = playerFactory('周杰', 'blue'); var player7 = playerFactory('贾乃亮', 'blue'); var player8 = playerFactory('李小璐', 'blue'); // 定义一个工厂来建立玩家 function playerFactory (name, teamColor){ var newPlayer = new Player(name, teamColor); // 建立新玩家 for(var i = 0 ; i < players.length ; i++){ // 通知全部玩家,有新角色加入 var player = players[i] if(player.teamColor === newPlayer.teamColor){ player.partners.push(newPlayer); newPlayer.partners.push(player); }else{ player.enemies.push(newPlayer); // 相互添加敌人列表 newPlayer.enemies.push(player); } } players.push(newPlayer); return newPlayer; } // 接着再改写构造函数Player, 使每一个玩家对象都增长一些属性,分别是队友列表,敌人列表,玩家当前的状态,角色名字,以及玩家所在队伍的颜色 function Player(name, teamColor){ this.partners = []; // 队友列表 this.enemies = []; // 敌人的状态 this.state = 'live'; // 玩家状态 this.name = name; // 角色名字 this.teamColor = teamColor; // 队伍颜色 } // 玩家胜利和失败以后的展示 Player.prototype.win = function(){ console.log('winner: ' + this.name) // 玩家团队胜利 }; Player.prototype.lose = function(){ console.log('lose: ' + this.name) // 玩家团队失败 }; // 玩家死亡以后要遍历本身的队友,若是队友所有死亡则游戏结束,这局游戏失败,同时通知敌人队伍的全部玩家取得胜利,代码以下 Player.prototype.die = function(){ // 玩家死亡 var all_dead = true; this.state = 'dead'; // 设置玩家为死亡状态 for(var i = 0, partner; partner = this.partners[i++];){ if(partner.state !== 'dead'){ // 若是还有一个队友没有死亡, 则游戏还未失败 all_dead = false; break; } } if(all_dead === true){ // 若是队友所有死亡 this.lose(); // 通知本身游戏失败 for(var i = 0; i < this.partners.length; i++){ // 通知全部队友玩家游戏失败 var partner = this.partners[i] partner.lose() } for(var i = 0; i < this.enemies.length; i++ ){ // 通知全部敌人游戏胜利 var enemy = this.enemies[i] enemy.win() } } } 复制代码
测试代码以下:
// 让红队玩家所有死亡
player1.die();
player2.die();
player3.die();
player4.die();
复制代码
咱们能够看到,每一个玩家都保存里了队友列表和对手列表,若是线上有1万我的,那这个列表就是很是大,每一个人都去遍历这个列表,开销太大了,因此咱们须要一种方法来解决这个问题,此时,中介者模式闪亮登场喽
// 如下的playerDirector就是中介者,咱们最后来实现它,先无论 // 从新定义Player类 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.reciveMessage('playerDead', this); // 给中介者发送消息,玩家死亡 }; // 移除玩家 Player.prototype.remove = function () { playerDirector.reciveMessage('removePlayer', this); // 给中介者发送消息,移除一个玩家 }; // 玩家换队 Player.prototype.changeTeam = function (color) { playerDirector.reciveMessage('changeTeam', this, color); // 给中介者发送消息,玩家换队 }; // 建立玩家的工厂函数改成 var playerFactory = function (name, teamColor) { var newPlayer = new Player(name, teamColor); // 创造一个新的玩家对象 playerDirector.reciveMessage('addPlayer', newPlayer); // 给中介者发送消息,新增玩家 return newPlayer; }; // 中介者用订阅发布这模式实现 var playerDirector = (function () { var players = {}, // 保存全部玩家 operations = {}; // 中介者能够执行的操做 // 新增一个玩家 operations.addPlayer = function (player) { var teamColor = player.teamColor; // 玩家的队伍颜色 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(); // 本队全部玩家 lose } for (var color in players) { if (color !== teamColor) { var teamPlayers = players[color]; // 其余队伍的玩家 for (var i = 0, player; player = teamPlayers[i++];) { player.win(); // 其余队伍全部玩家 win } } } } }; var reciveMessage = function () { var message = Array.prototype.shift.call(arguments); // arguments 的第一个参数为消息名称 operations[message].apply(this, arguments); }; return { reciveMessage: reciveMessage } })(); 复制代码
测试代码
咱们来看下测试结果: // 红队: var player1 = playerFactory( '皮蛋', 'red' ), player2 = playerFactory( '小乖', 'red' ), player3 = playerFactory( '宝宝', 'red' ), player4 = playerFactory( '小强', 'red' ); // 蓝队: var player5 = playerFactory( '黑妞', 'blue' ), player6 = playerFactory( '葱头', 'blue' ), player7 = playerFactory( '胖墩', 'blue' ), player8 = playerFactory( '海盗', 'blue' ); player1.die(); player2.die(); player3.die(); player4.die(); 复制代码
// 数组生成迭代器须要调用它的Symbol.iterator属性,就能够以此遍历这个数组的每一项了 let arr = ['a', 'b', 'c']; let iter = arr[Symbol.iterator](); iter.next() // { value: 'a', done: false } iter.next() // { value: 'b', done: false } iter.next() // { value: 'c', done: false } iter.next() // { value: undefined, done: true } 复制代码
咱们举例的场景大概是:公司针对支付过定金的用户有必定的优惠政策。在正式购买后,已经支付过500元定金的用户会收到100元的商城优惠券,200元定金的用户能够收到50元的优惠券,而以前没有支付定金的用户只能进入普通购买模式,也就是没有商城优惠券,且在库存有限的状况下不必定保证能买到。
如下是咱们须要的几个字段
// 实现代码以下 var order = function(orderType, pay, stock){ if(orderType === 1){ // 500元定金购买模式 if(pay === true ){ // 已支付定金 console.log('500元定金预购,获得100元优惠券') }else{ if(stock > 0){ // 用于普通购买模式 console.log('普通购买,无优惠券'); }else{ console.log('手机库存不足') } } } else if (orderType === 1){ // 200元定金购买模式 if(pay === true ){ console.log('200元定金预购,获得100元优惠券') }else{ if(stock > 0){ console.log('普通购买,无优惠券'); }else{ console.log('手机库存不足') } } } else if (orderType === 3){ // 普通购买模式 if(stock > 0){ console.log('普通购买,无优惠券'); }else{ console.log('手机库存不足') } } } order(1, true, 500) // 输出: 500元定金预购,获得100元优惠券 复制代码
这段代码很繁琐,难以阅读,通常状况下,我会想到用策略模式,把if条件变为一个个的算法,来减小大量的if...else的判断
// 500元订单 var order500 = function(orderType, pay, stock){ if(orderType === 1 && pay === true){ console.log('500元定金预购,获得100元优惠券') }else{ order200(orderType, pay, stock); // 将请求传给200元的订单 } } // 200元订单 var order200 = function(orderType, pay, stock){ if(orderType === 2 && pay === true){ console.log('200元定金预购,获得50元优惠券') }else{ order200(orderType, pay, stock); // 将请求传给普通订单 } } // 普通订单 var orderNormal = function(orderType, pay, stock){ if(stock > 0){ console.log('普通购买,无优惠券') }else{ console.log('手机库存不足') } } // 测试结果 order500(1, true, 500); // 输出: 500元定金预购,获得100优惠券 复制代码
接下来,咱们更深一步,毕竟用责任链模式来改造
// 500元订单 var order500 = function(orderType, pay, stock){ if(orderType === 1 && pay === true){ console.log('500元定金预购,获得100元优惠券') }else{ return 'nextSuccessor' } } // 200元订单 var order200 = function(orderType, pay, stock){ if(orderType === 2 && pay === true){ console.log('200元定金预购,获得50元优惠券') }else{ return 'nextSuccessor' } } // 普通订单 var orderNormal = function(orderType, pay, stock){ if(stock > 0){ console.log('普通购买,无优惠券') }else{ console.log('手机库存不足') } } 复制代码
接着咱们写一个链条的类,用来造成咱们的链条
var Chain = function( fn ){ this.fn = fn; this.successor = null } Chain.prototype.setNextSuccessor = function(successor){ // 这个方法是把请求给链条的下一个 return this.successor = successor } Chain.prototype.passRequest = function(...args){ // 传递请求给某个节点 var ret = this.fn(...args); if(ret === 'nextSuccessor'){ return this.successor && this.successor.passRequest(...args) } return ret } 复制代码
如今咱们把3个订单函数分别包装成责任链的节点:
var chainOrder500 = new Chain(order500);
var chainOrder200 = new Chain(order200);
var chainOrderNormal = new Chain(orderNormal);
复制代码
而后指定节点在职责链中的顺序
chainOrder500.setNextSuccessor(chainOrder200);
chainOrder200.setNextSuccessor(chainOrderNormal);
复制代码
最后把请求传给第一个节点
chainOrder500.passRequest(1, true, 500); // 输出:500元定金预购,获得100元优惠券 chainOrder500.passRequest(2, true, 500); // 输出:200元定金预购,获得50元优惠券 chainOrder500.passRequest(3, true, 500); // 输出:普通购买,无优惠券 chainOrder500.passRequest(1, false, 0); // 输出:手机库存不足 复制代码
拓展: 异步责任链模式
Chain.prototype.next = function(...args){ return this.successor && this.successor.passRequest(...args) } 复制代码
异步函数以下
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() }) }); var fn3 = new Chain(function(){ console.log(3); }); // 测试 fn1.setNextSuccessor(fn2).setNextSuccessor(fn3); fn1.passRequest() 复制代码