参考 JavaScript 中常见的十五种设计模式(上) JavaScript 中常见的十五种设计模式(下)
单例模式 定义 保证一个类仅有一个实例,并提供一个访问它的全局访问点
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 function SetManager (name ) { this .manager = name; } SetManager .prototype .getName = function ( ) { console .log (this .manager ); }; function getSingleton (fn ) { let instance = null ; return function ( ) { if (!instance) { instance = fn.apply (this , arguments ); console .log ("arguments: " + JSON .stringify (arguments )); } return instance; }; } let managerSingleton = getSingleton (function (name ) { let manager = new SetManager (name); return manager; }); managerSingleton ("a" ).getName (); managerSingleton ("b" ).getName (); managerSingleton ("c" ).getName ();
策略模式 定义 定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换
分离 使用 ← 分离 → 实现
组成
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 let errorMsgs = { default : "输入数据格式不正确" , minLength : "输入数据长度不足" , isNumber : "请输入数字" , required : "内容不为空" , }; let rules = { minLength : function (value, length, errorMsg ) { if (value.length < length) { return errorMsg || errorMsgs["minLength" ]; } }, isNumber : function (value, errorMsg ) { if (!/\d+/ .test (value)) { return errorMsg || errorMsgs["isNumber" ]; } }, required : function (value, errorMsg ) { if (value === "" ) { return errorMsg || errorMsgs["required" ]; } }, }; function Validator ( ) { this .items = []; } Validator .prototype = { constructor : Validator , add : function (value, rule, errorMsg ) { let arg = [value]; if (rule.indexOf ("minLength" ) !== -1 ) { let temp = rule.split (":" ); arg.push (temp[1 ]); rule = temp[0 ]; } arg.push (errorMsg); this .items .push (function ( ) { return rules[rule].apply (this , arg); }); }, start : function ( ) { for (let i = 0 ; i < this .items .length ; ++i) { let ret = this .items [i](); if (ret) { console .log (ret); } } }, }; function testTel (val ) { return val; } let validate = new Validator ();validate.add (testTel ("ccc" ), "isNumber" , "只能为数字" ); validate.add (testTel ("" ), "required" ); validate.add (testTel ("123" ), "minLength:5" , "最少5位" ); validate.add (testTel ("12345" ), "minLength:5" , "最少5位" ); let ret = validate.start ();console .log (ret);
代理模式 保护代理 虚拟代理 在控制对主体的访问时,加入了一些额外的操作
缓存代理 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 throttle (fn, delay, mustRunDelay ) { let timer = null let t_start return function ( ) { let context = this , args = arguments , t_curr = +new Date () clearTimeout (timer) if (!t_start) { t_start = t_curr } if (t_curr - t_start >= mustRunDelay) { fn.apply (context, args) t_start = t_curr } else { timer = setTimeout (function ( ) { fn.apply (context, args) }, delay) } } } function add ( ) { let arg = [].slice .call (arguments ); return arg.reduce ((a, b ) => { a + b }); } let proxyAdd = (function ( ) { let cache = []; return function ( ) { let arg = [].slice .call (arguments ).join (',' ); if (cache[arg]) { return cache[arg]; } else { let ret = add.apply (this , arguments ); return ret; } }; })(); console .log ( add (1 , 2 , 3 , 4 ), add (1 , 2 , 3 , 4 ), proxyAdd (10 , 20 , 30 , 40 ), proxyAdd (10 , 20 , 30 , 40 ) );
迭代器模式 迭代器模式是指提供一种方法 顺序访问 一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示
在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺序访问其中的每个元素
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 function createIterator (items ) { let i = 0 ; return { next : function ( ) { let done = i >= items.length ; let value = !done ? items[i++] : undefined ; return { done : done, value : value, }; }, }; } let iterator = createIterator ([1 , 2 , 3 ]);console .log (iterator.next ()); console .log (iterator.next ()); console .log (iterator.next ()); console .log (iterator.next ()); console .log (iterator.next ()); function year2000 ( ) { let year = new Date ().getFullYear (); if (year <= 2000 ) { console .log ("A" ); } return false ; } function year2100 ( ) { let year = new Date ().getFullYear (); if (year >= 2100 ) { console .log ("C" ); } return false ; } function year ( ) { let year = new Date ().getFullYear (); if (year > 2000 && year < 2100 ) { console .log ("B" ); } return false ; } function iteratorYear ( ) { for (let i = 0 ; i < arguments .length ; ++i) { let ret = arguments [i](); if (ret !== false ) { return ret; } } } let manager = iteratorYear (year2000, year2100, year);
发布-订阅模式 也称作观察者模式
eg 小 A 在公司 C 完成了笔试及面试,小 B 也在公司 C 完成了笔试。他们焦急地等待结果,每隔半天就电话询问公司 C,导致公司 C 很不耐烦。
一种解决办法是 AB 直接把联系方式留给 C,有结果的话 C 自然会通知 AB
这里的询问属于显示 调用,留给属于 订阅,通知属于 发布
优缺点 优 一为时间上的解耦,二为对象之间的解耦
缺
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 let observer = { subscribes : [], subscribe : function (type, fn ) { if (!this .subscribes [type]) { this .subscribes [type] = []; } typeof fn === "function" && this .subscribes [type].push (fn); }, publish : function ( ) { let type = [].shift .call (arguments ), fns = this .subscribes [type]; console .log ("发布 " , fns); if (!fns || !fns.length ) { return ; } for (let i = 0 ; i < fns.length ; ++i) { fns[i].apply (this , arguments ); } }, remove : function (type, fn ) { if (typeof type === "undefined" ) { this .subscribes = []; return ; } let fns = this .subscribes [type]; if (!fns || !fns.length ) { return ; } if (typeof fn === "undefined" ) { fns.length = 0 ; return ; } for (let i = 0 ; i < fns.length ; ++i) { if (fns[i] === fn) { fns.splice (i, 1 ); } } }, }; function jobListForA (jobs ) { console .log ("A" , jobs); } function jobListForB (jobs ) { console .log ("B" , jobs); } observer.subscribe ("job" , jobListForA); observer.subscribe ("job" , jobListForB); observer.subscribe ("examinationA" , function (score ) { console .log (score); }); observer.subscribe ("examinationB" , function (score ) { console .log (score); }); observer.subscribe ("interviewA" , function (result ) { console .log (result); }); observer.publish ("examinationA" , 100 ); observer.publish ("examinationB" , 80 ); observer.publish ("interviewA" , "备用" ); observer.publish ("job" , ["前端" , "后端" , "测试" ]); observer.remove ("examinationB" ); observer.remove ("job" , jobListForA); observer.publish ("examinationB" , 80 ); observer.publish ("job" , ["前端" , "后端" , "测试" ]);
命令模式 实现 简单的命令模式实现可以直接使用 对象字面量 的形式定义一个命令
1 2 3 4 5 let incrementCommand = { execute : function ( ) { }, };
采用对象创建处理的方式
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 function IncrementCommand ( ) { this .val = 0 ; this .stack = []; this .stackPosition = -1 ; } IncrementCommand .prototype = { constructor : IncrementCommand , execute : function ( ) { this ._clearRedo (); let command = function ( ) { this .val += 2 ; }.bind (this ); command (); this .stack .push (command); this .stackPosition ++; this .getValue (); }, canUndo : function ( ) { return this .stackPosition >= 0 ; }, canRedo : function ( ) { return this .stackPosition < this .stack .length - 1 ; }, undo : function ( ) { if (!this .canUndo ()) { return ; } this .stackPosition --; let command = function ( ) { this .val -= 2 ; }.bind (this ); command (); this .getValue (); }, redo : function ( ) { if (!this .canRedo ()) { return ; } this .stack [++this .stackPosition ](); this .getValue (); }, _clearRedo : function ( ) { this .stack = this .stack .slice (0 , this .stackPosition + 1 ); }, getValue : function ( ) { console .log (this .val ); }, };
组合模式 使用组合模式来实现扫描文件夹中的文件
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 function Folder (name ) { this .name = name; this .parent = null ; this .files = []; } Folder .prototype = { constructor : Folder , add : function (file ) { file.parent = this ; this .files .push (file); return this ; }, scan : function ( ) { for (let i = 0 ; i < this .files .length ; ++i) { this .files [i].scan (); } }, remove : function (file ) { if (typeof file === "undefined" ) { this .files = []; return ; } for (let i = 0 ; i < this .files .length ; ++i) { if (this .files [i] === file) { this .files .splice (i, 1 ); } } }, }; function File (name ) { this .name = name; this .parent = null ; } File .prototype = { constructor : File , add : function ( ) { console .log ("文件里面不能添加文件" ); }, scan : function ( ) { let name = [this .name ]; let parent = this .parent ; while (parent) { name.unshift (parent.name ); parent = parent.parent ; } console .log (name.join (" / " )); }, };
实例化,在组合对象中插入组合或叶对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 let web = new Folder ("Web" );let fe = new Folder ("前端" );let css = new Folder ("CSS" );let js = new Folder ("js" );let rd = new Folder ("后端" );web.add (fe).add (rd); let file1 = new File ("HTML权威指南.pdf" );let file2 = new File ("CSS权威指南.pdf" );let file3 = new File ("JavaScript权威指南.pdf" );let file4 = new File ("MySQL基础.pdf" );let file5 = new File ("Web安全.pdf" );let file6 = new File ("Linux菜鸟.pdf" );css.add (file2); fe.add (file1).add (file3).add (css).add (js); rd.add (file4).add (file5); web.add (file6); rd.remove (file4); web.scan ();
模板方法模式 模板方法模式一般的实现方式为继承
以运动作为例子,运动有比较通用的一些处理,这部分可以抽离开来,在父类中实现。具体某项运动的特殊性则有自类来重写实现。
最终子类直接调用父类的模板函数来执行
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 function Sport ( ) {}Sport .prototype = { constructor : Sport , init : function ( ) { this .stretch (); this .jog (); this .deepBreath (); this .start (); let free = this .end (); if (free !== false ) { this .stretch (); } }, stretch : function ( ) { console .log ("拉伸" ); }, jog : function ( ) { console .log ("慢跑" ); }, deepBreath : function ( ) { console .log ("深呼吸" ); }, start : function ( ) { throw new Error ("子类必须重写此方法" ); }, end : function ( ) { console .log ("运动结束" ); }, }; function Basketball ( ) {}Basketball .prototype = new Sport ();Basketball .prototype .start = function ( ) { console .log ("先投上几个三分" ); }; Basketball .prototype .end = function ( ) { console .log ("运动结束了,有事先走一步" ); return false ; }; function Marathon ( ) {}Marathon .prototype = new Sport ();let basketball = new Basketball ();let marathon = new Marathon ();basketball.init (); marathon.init ();
享元模式 享元(flyweight)模式是一种用于性能优化的模式,它的目标是尽量减少共享对象的数量
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 function Fitness (sex ) { this .sex = sex; } let FitnessFactory = { objs : [], create : function (sex ) { if (!this .objs [sex]) { this .objs [sex] = new Fitness (sex); } return this .objs [sex]; }, }; let FitnessManager = { fitnessData : {}, add : function (name, sex, age, height, weight ) { let fitness = FitnessFactory .create (sex); this .fitnessData [name] = { age : age, height : height, weight : weight, }; return fitness; }, updateFitnessData : function (name, obj ) { let fitnessData = this .fitnessData [name]; for (let item in fitnessData) { if (fitnessData.hasOwnProperty (item)) { obj[item] = fitnessData[item]; } } }, }; Fitness .prototype .judge = function (name ) { FitnessManager .updateFitnessData (name, this ); let ret = name + ": " ; if (this .sex === "male" ) { ret += this .judgeMale (); } else { ret += this .judgeFemale (); } console .log (ret); }; Fitness .prototype .judgeMale = function ( ) { let ratio = this .height / this .weight ; return this .age > 20 ? ratio > 3.5 : ratio > 2.8 ; }; Fitness .prototype .judgeFemale = function ( ) { let ratio = this .height / this .weight ; return this .age > 20 ? ratio > 4 : ratio > 3 ; }; let a = FitnessManager .add ("A" , "male" , 18 , 160 , 80 );let b = FitnessManager .add ("B" , "male" , 21 , 180 , 70 );let c = FitnessManager .add ("C" , "female" , 28 , 160 , 80 );let d = FitnessManager .add ("D" , "male" , 18 , 170 , 60 );let e = FitnessManager .add ("E" , "female" , 18 , 160 , 40 );a.judge ("A" ); b.judge ("B" ); c.judge ("C" ); d.judge ("D" ); e.judge ("E" );
职责链模式 定义 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链 传递该请求,直到有一个对象处理
它为止
核心 请求发送者只需要知道链中的第一个节点,弱化发送者和一组接收者之间的强联系,可以便捷地在职责链中增加或删除一个节点,同样地,指定谁是第一个节
点也很便捷
实现 展示不同类型的变量
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 function ChainItem (fn ) { this .fn = fn; this .next = null ; } ChainItem .prototype = { constructor : ChainItem , setNext : function (next ) { this .next = next; console .log ("next: " + next); return next; }, start : function ( ) { this .fn .apply (this , arguments ); }, toNext : function ( ) { if (this .next ) { this .start .apply (this .next , arguments ); } else { console .log ("无匹配的执行项目" ); } }, }; function showNumber (num ) { if (typeof num === "number" ) { console .log ("number" , num); } else { this .toNext (num); console .log ("数字后下一步" ); } } function showString (str ) { if (typeof str === "string" ) { console .log ("string" , str); } else { this .toNext (str); } } function showObject (obj ) { if (typeof obj === "object" ) { console .log ("object" , obj); } else { this .toNext (obj); } } let chainNumber = new ChainItem (showNumber);let chainString = new ChainItem (showString);let chainObject = new ChainItem (showObject);let next1 = chainObject.setNext (chainNumber);console .log ("next1: " + JSON .stringify (next1)); let next2 = next1.setNext (chainString);console .log ("next2: " + JSON .stringify (next2)); chainString.start ("12" ); chainNumber.start ({}); chainObject.start ({}); chainObject.start (123 );
这时想判断未定义的时候呢,直接加到链中即可
1 2 3 4 5 6 7 8 9 10 11 12 13 function showUndefined (obj ) { if (typeof obj === "undefined" ) { console .log ("undefined" ); } else { this .toNext (obj); } } let chainUndefined = new ChainItem (showUndefined);chainString.setNext (chainUndefined); chainNumber.start ();
对象增多,结构更清晰,一定程度上可能影响性能,注意避免过长的职责链
中介者模式 定义 所有的相关 对象都通过中介者对象来通信,而不是互相引用,所以当一个对象发生改变时,只需要通知中介者对象即可
核心 使网状的多对多关系变成了相对简单的一对多关系(复杂的调度处理都交给中介者)
实现 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 let A = { score : 10 , changeTo : function (score ) { this .score = score; this .getRank (); }, getRank : function ( ) { let scores = [this .score , B.score , C.score ].sort (function (a, b ) { return a < b; }); console .log (scores.indexOf (this .score ) + 1 ); }, }; let B = { score : 20 , changeTo : function (score ) { this .score = score; rankMediator (B); }, }; let C = { score : 30 , changeTo : function (score ) { this .score = score; rankMediator (C); }, }; function rankMediator (person ) { let scores = [A.score , B.score , C.score ].sort (function (a, b ) { return a < b; }); console .log (scores.indexOf (person.score ) + 1 ); } A.changeTo (100 ); B.changeTo (200 ); C.changeTo (50 );
虽然中介者做到了对模块和对象的解耦,但有时对象之间的关系并非一定要解耦,强行使用中介者来整合,可能会使代码更为繁琐,需要注意
装饰者模式 1. 定义 以动态地给某个对象添加一些额外的职责,而不会影响从这个类中派生的其他对象。 是一种“即用即付”的方式,能够在不改变对 象自身的基础上,在程序运行期间给对象动态地 添加职责
2. 核心 是为对象动态加入行为,经过多重包装,可以形成一条装饰链
3. 实现 最简单的装饰者,就是重写对象的属性
1 2 3 4 5 let A = { score : 10 , }; A.score = "分数:" + A.score ;
可以使用传统面向对象的方法来实现装饰,添加技能
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 function Person ( ) {}Person .prototype .skill = function ( ) { console .log ("数学" ); }; function MusicDecorator (person ) { this .person = person; } MusicDecorator .prototype .skill = function ( ) { this .person .skill (); console .log ("音乐" ); }; function RunDecorator (person ) { this .person = person; } RunDecorator .prototype .skill = function ( ) { this .person .skill (); console .log ("跑步" ); }; let person = new Person ();let person1 = new MusicDecorator (person);person1 = new RunDecorator (person1); person.skill (); person1.skill ();
更简洁的写法
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 function decoratorBefore (fn, beforeFn ) { return function ( ) { let ret = beforeFn.apply (this , arguments ); if (ret !== false ) { fn.apply (this , arguments ); } }; } function skill ( ) { console .log ("数学" ); } function skillMusic ( ) { console .log ("音乐" ); } function skillRun ( ) { console .log ("跑步" ); } let skillDecorator = decoratorBefore (skill, skillMusic);skillDecorator = decoratorBefore (skillDecorator, skillRun); skillDecorator ();
状态模式 1. 定义 事物内部状态的改变往往会带来事物的行为改变。在处理的时候,将这个处理委托给当前的 状态对象 即可,该状态对象会负责渲染它自身的行为
2. 核心 区分事物内部的状态,把事物的每种状态都封装成单独的类,跟此种状态有关的行为都被封装在这个类的内部
3. 实现 以一个人的工作状态作为例子,在刚醒、精神、疲倦几个状态中切换着
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 function Work (name ) { this .name = name; this .currentState = null ; this .wakeUpState = new WakeUpState (this ); this .energeticState = new EnergeticState (this ); this .tiredState = new TiredState (this ); this .init (); } Work .prototype .init = function ( ) { this .currentState = this .wakeUpState ; document .body .onclick = () => { this .currentState .behaviour (); }; }; Work .prototype .setState = function (state ) { this .currentState = state; }; function WakeUpState (work ) { this .work = work; } WakeUpState .prototype .behaviour = function ( ) { console .log (this .work .name , ":" , "刚醒呢,睡个懒觉先" ); setTimeout (() => { this .work .setState (this .work .energeticState ); }, 2 * 1000 ); }; function EnergeticState (work ) { this .work = work; } EnergeticState .prototype .behaviour = function ( ) { console .log (this .work .name , ":" , "超级精神的" ); setTimeout (() => { this .work .setState (this .work .tiredState ); }, 1000 ); }; function TiredState (work ) { this .work = work; } TiredState .prototype .behaviour = function ( ) { console .log (this .work .name , ":" , "怎么肥事,好困" ); setTimeout (() => { this .work .setState (this .work .wakeUpState ); }, 1000 ); }; let work = new Work ("曹操" );
4. 优缺点 优点 状态切换逻辑分布在状态类中,易于维护
缺点 多个状态类,影响性能,可用享元模式进一步优化
将逻辑分散在状态类中,不易看出状态机变化逻辑
十四、适配器模式 定义 是解决两个软件实体间的接口不兼容的问题,对不兼容的部分进行适配
实现 比如一个简单的数据格式转换的适配器
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 function renderData (data ) { data.forEach (function (item ) { console .log (item); }); } function arrayAdapter (data ) { if (typeof data !== "object" ) { return []; } if (Object .prototype .toString .call (data) === "[object Array]" ) { return data; } let temp = []; for (let item in data) { if (data.hasOwnProperty (item)) { temp.push (data[item]); } } return temp; } let data = { 0 : "A" , 1 : "B" , 2 : "C" , }; renderData (arrayAdapter (data));
外观模式 1. 定义 为子系统中的一组接口提供一个一致的界面,定义一个高层接口,这个接口使子系统更加容易使用
2. 核心 可以通过请求外观接口来达到访问子系统,也可以选择越过外观来直接访问子系统
3. 实现 外观模式在 JS 中,可以认为是一组函数的集合
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 function start ( ) { console .log ("start" ); } function doing ( ) { console .log ("doing" ); } function end ( ) { console .log ("end" ); } function execute ( ) { start (); doing (); end (); } function init ( ) { execute (); } init ();