目录
前言 本文参考于《javascript模式》,因此会大量内容会和书中相同,手上有这本书的朋友可以直接看书。因为我的记忆习惯是抄书,所以我会先抄写下来再发到博客上。
单体模式 单体模式思想在于保证一个特定类仅有一个实例,意味着当你第二次使用同一个类创建信对象时,应得到和第一次创建对象完全相同。
方法一 1 2 3 4 5 6 7 8 9 10 11 12 function Universe ( ) { if (typeof Universe.instance==="object" ){ return Universe.instance; } this .xx="xx" ; Universe.instance=this ; return this ; } var uni=new Universe();var uni2=new Universe();uni===uni2;
缺点 instance 属性暴露。
方法二 使用闭包
1 2 3 4 5 6 7 8 9 10 11 function Universe ( ) { var instance=this ; this .xx="xx" ; Universe=function ( ) { return instance; } } var uni=new Universe();var uni2=new Universe();uni===uni2;
缺点 因为重写了构造函数,constructor 还是指向了老的构造函数,且实例化后在添加原型属性也是不一样的。如下
1 2 3 4 5 6 7 var uni = new Universe();Universe.prototype.a = 1 var uni2 = new Universe();console .log(uni === uni2) console .log(uni.a) console .log(uni2.a) console .log(uni.constructor === Universe);
方法三 解决方法二问题。
1 2 3 4 5 6 7 8 9 10 function Universe ( ) { var instance; Universe=function Universe ( ) { return instance ; } Universe.prototype=this ; instance=new Universe(); instance.constructor=Universe; instance.xx="xx" ; }
方法四 自运行函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 var Universe;(function ( ) { var instance; Universe=function Universe ( ) { if (instance){ return instance; } instance=this ; this .xx="xx" ; } })(); var uni = new Universe();Universe.prototype.a = 1 var uni2 = new Universe();console .log(uni === uni2) console .log(uni.a) console .log(uni2.a) console .log(uni.constructor === Universe);
工厂模式 工厂模式是为了创建对象。
例子
公共构造函数 CarMaker
名为factory的CarMaker静态方法来创建car对象
1 2 3 4 5 6 var corolla=CarMaker.factory('compact' );var solstice=CarMaker.factory('convertible' );var cherokee=CarMaker.factory('suv' );corolla.drive() solstice.drive() cherokee.drive()
实现 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 function CarMaker ( ) {}CarMaker.prototype.drive = function ( ) { return "I have " + this .doors + " doors" ; } CarMaker.compact = function ( ) { this .doors = 4 ; } CarMaker.convertible = function ( ) { this .doors = 2 } CarMaker.suv = function ( ) { this .doors = 6 ; } CarMaker.factory = function (type ) { if (typeof CarMaker[type] !== "function" ) { throw "Error" } if (typeof CarMaker[type].prototype.drive !== "function" ) { CarMaker[type].prototype = new CarMaker(); } var newCar = new CarMaker[type](); return newCar; } var corolla = CarMaker.factory('compact' );console .log(corolla.drive());
内置工厂对象 Object() 构造函数即为内置工厂对象。
迭代器模式 有一个包含某种数据集合的对象,该数据可能存储在一个复杂数据结构内部,而要提供一个简单方法讷讷感访问到数据结构中没一个元素。
next() 下一个
hasNext() 是否有下一个
reWind() 重置指针
current() 返回当前
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 agg = (function ( ) { var index = 0 ; var data = [1 , 2 , 3 , 4 , 5 , 6 ]; var length = data.length; return { next: function ( ) { if (!this .hasNext()) { return null ; } var element = data[index]; index++; return element; }, hasNext: function ( ) { return index < length; }, reWind: function ( ) { index = 0 ; }, current: function ( ) { return data[index]; } } })(); while (agg.hasNext()) { console .log(agg.next()); } agg.reWind();
装饰者模式 可以在运行时候添加附加功能到对象中,他的一个方便特征在于其预期行为的可定制和可配置特性。
例子 假设在开发一个销售商品的Web应用,每一笔信销售都是一个人新的 sale 对象。该对象“知道”有关项目的价格,并可以通过 getPrice() 方法返回加个。 根据不同情况,可以用额外的功能装饰此对象。 假设客户在魁北克省,买房需要支付联邦税和魁北克省税,则此时需要调用联邦税装饰者和魁北克省税装饰者。
1 2 3 4 5 var sale=new Sale(100 );sale=sale.decorate("fedtax" ); sale=sale.decorate("quebec" ); sale=sale.decorate("miney" ); sale.getPrice();
并且装饰是可选的,例如不再魁北克省有可能没有省税。
方法一 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 function Sale (price ) { this .price = price; } Sale.prototype.getPrice = function ( ) { return this .price; }; Sale.decorators = {}; Sale.decorators.fedtax = { getPrice: function ( ) { var price = this .uber.getPrice(); return price * 0.8 ; }, } Sale.decorators.quebec = { getPrice: function ( ) { var price = this .uber.getPrice(); return price * 0.7 ; }, } Sale.decorators.money = { getPrice: function ( ) { var price = this .uber.getPrice(); return "$" + price * 0.9 ; }, } Sale.prototype.decorate = function (decorate ) { var F = function ( ) {}; var overrides = this .constructor.decorators[decorate]; F.prototype = this ; var newobj = new F(); newobj.uber = F.prototype; for (var key in overrides) { if (overrides.hasOwnProperty) { newobj[key] = overrides[key]; } } return newobj; }; var sale = new Sale(100 );sale = sale.decorate("fedtax" ); sale = sale.decorate("quebec" ); sale = sale.decorate("money" ); console .log(sale.getPrice());
方法二 此方法使用列表实现,而且相对来说比较好理解一点。本质就是把装饰者名称保存到一个列表中并且一次调用此列表中的方法。
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 function Sale (price ) { this .price = price; this .decorateList = []; } Sale.decorators = {}; Sale.decorators.fedtax = { getPrice: function (price ) { var price = this .uber.getPrice(); return price * 0.8 ; }, } Sale.decorators.quebec = { getPrice: function (price ) { var price = this .uber.getPrice(); return price * 0.7 ; }, } Sale.decorators.money = { getPrice: function (price ) { var price = this .uber.getPrice(); return "$" + price * 0.9 ; }, } Sale.prototype.decorate = function (decorator ) { this .decorateList.push(decorator); }; Sale.prototype.getPrice = function ( ) { var price = this .price; this .decorateList.forEach(function (name ) { price = Sale.decorators[name].getPrice(price); }); return price; }; var sale = new Sale(100 );sale = sale.decorate("fedtax" ); sale = sale.decorate("quebec" ); sale = sale.decorate("money" ); console .log(sale.getPrice());
策略模式 策略模式支持在运行时候选择算法。例如用在表单验证问题上,可以创建一个具有 validate() 方法的验证器对象,无论表单具体类型是什么,该方法都会被调用, 并且返回结果或者错误信息。
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 validator = { types: {}, messages: [], config: {}, validate: function (data ) { var i, msg, type, checker, result_ok; this .messages = []; for (i in data) { if (data.hasOwnProperty(i)) { type = this .config[i]; checker = this .types[type]; if (!type) { continue ; } if (!checker) { throw { name: "ValidationError" , message: "No handler to validate type " + type }; } result_ok = checker.validate(data[i]); if (!result_ok) { msg = "Invalid value for *" + i + "*, " + checker.instructions; this .messages.push(msg); } } } return this .hasErrors(); }, hasErrors: function ( ) { return this .messages.length !== 0 ; } }; validator.types.isNonEmpty = { validate: function (value ) { return value !== "" ; }, instructions: "传入的值不能为空" }; validator.types.isNumber = { validate: function (value ) { return !isNaN (value); }, instructions: "传入的值只能是合法的数字,例如:1, 3.14 or 2010" }; validator.types.isAlphaNum = { validate: function (value ) { return !/[^a-z0-9]/i .test(value); }, instructions: "传入的值只能保护字母和数字,不能包含特殊字符" }; var data = { first_name: "Tom" , last_name: "Xu" , age: "unknown" , username: "TomXu" }; validator.config = { first_name: 'isNonEmpty' , age: 'isNumber' , username: 'isAlphaNum' }; validator.validate(data); if (validator.hasErrors()) { console .log(validator.messages.join("\n" )); }
策略模式定义及例子实现参考与《javascript模式》及 汤姆大叔的博客
外观模式 外观模式即让多个方法一起被调用
例如。 stopPropagation() 和 preventDefault() 兼容性一起调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var myEvent = { stop: function (e ) { if (typeof e.preventDefault() === "function" ) { e.preventDefault(); } if (typeof e.stopPropagation() === "function" ) { e.stopPropagation(); } if (typeof e.returnValue === "boolean" ) { e.returnValue = false ; } if (typeof e.cancelBubble === "boolean" ) { e.cancelBubble = true ; } } }
代理模式 在代理模式中,一个对象充当另外一个对象的接口,和外观模式区别是:外观模式是合并调用多个方法。 代理模式是介于对象的客户端和对象本身之间,并且对该对象的访问进行保护。
包裹例子 现在有个包裹,卖家要把这个包裹寄给gary,则需要通过快递公司寄过来,此时快递公司就是一个 proxy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 var package = function (receiver ) { this .receiver = receiver; } var seller = function (package ) { this .package = package; this .send = function (gift ) { return package.receiver + "你的包裹:" + gift; } } var express = function (package ) { this .package = package; this .send = function (packageName ) { return new seller(package).send(packageName); } } var ems = new express(new package("gary" ));console .log(ems.send("键盘" ));
论坛权限管理例子 本例子参考与 大熊君
用户
代码
权限
注册用户
001
1 4
论坛管理员
002
2 3 4
系统管理员
003
1 2 3 4
游客
000
null
用户类 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 User (name, code ) { this .name = name; this .code = code; } User.prototype.getName = function ( ) { return this .name; }; User.prototype.getCode = function ( ) { return this .code; }; User.prototype.post = function ( ) { }; User.prototype.remove = function ( ) { }; User.prototype.check = function ( ) { }; User.prototype.comment = 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 function Forum (user ) { this .user=user; } Forum.prototype.getUser = function ( ) { return this .user; }; Forum.prototype.post = function ( ) { var code=this .user.getCode(); if (code=="001" ||code=="003" ){ return this .user.post(); }else { return false ; } }; Forum.prototype.remove = function ( ) { var code=this .user.getCode(); if (code=="002" ||code=="003" ){ return this .user.remove(); }else { return false ; } }; Forum.prototype.check = function ( ) { var code=this .user.getCode(); if (code=="002" ||code=="003" ){ return this .user.check(); }else { return false ; } }; Forum.prototype.comment = function ( ) { var code=this .user.getCode(); if (code=="001" ||code=="002" ||code=="003" ){ return this .user.comment(); }else { return false ; } };
运行 1 new Forum(new User("administartor" ,"003" ));
中介者模式 中介者模式可以让多个对象之间松耦合,并降低维护成本
例如:游戏程序,两名玩家分别给与半分钟时间来竞争决出胜负(谁按键的次数多胜出,这里玩家1按1,玩家2按0)
计分板(scoreboard)
中介者 (mediator)
中介者知道所有其他对象的信息。他与输入设备(此时是键盘)进行通信并处理键盘上的按键时间,之后还将消息通知玩家。玩家玩游戏同时(每一分都更新自己分数)还要 通知中介者他所做的事情。中介者将更新后的分数传达给计分板。
除了中介者莫有对象知道其他对象。
图示 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 function Player (name ) { this .points = 0 ; this .name = name; } Player.prototype.play = function ( ) { this .points += 1 ; mediator.played(); }; var scoreboard = { element: "这里是获取的element用于展示分数" , update: function (score ) { var msg; for (var key in score) { if (score.hasOwnProperty(key)) { msg += score[key]; } } this .element.innerText = msg; }, } var mediator = { players: {}, setup: function ( ) { var players = this .players; players.home = new Player("home" ); players.guest = new Player('guest' ); }, played: function ( ) { var players = this .players; var score = { home: players.home.points, guest: players.guest.points } }, keypress: function (e ) { e = e || window .event; if (e.which === 49 ) { mediator.players.home.play(); return ; } if (e.which === 48 ) { mediator.player.guest.play(); return ; } }, } mediator.setup(); window .onkeypress = mediator.keypress;setTimeout(function ( ) { window .onkeypress = null ; alert("game end" ); }, 30000 );
观察者模式 观察者模式在 javascript 中使用非常广泛。所有的浏览器时间就是该模式的实现,node.js中的events也是此模式实现。 此模式另一个名称是 订阅/发布模式
。 设计这种模式原因是促进形成松散耦合,在这种模式中,并不是一个对象调用另一个对象的方法,而是一个对象订阅另一个对象的 特定活动并在状态改编后获得通知。订阅者因此也成为观察者,而被观察的对象成为发布者或者主题。当发生了一个重要事件时候 发布者会通知(调用)所有订阅者并且可能经常已事件对象的形式传递消息。
例子 我之前有篇文章即是观察者模式例子
nodejs的EventEmitter
小结 针对一个类仅创建一个对象。
根据字符串制定类型在运行时创建对象的方法。
提供一个API来遍历或者操作复杂的自定义数据结构。
通过从预定义装饰者对象中添加功能,从而在运行时侯调整对象
在悬在最佳策略以处理特定任务的时候仍然保持相同的接口。
通过把常用方法包装到一个新方法中,从来提供一个更为便利的API。
通过包装一个对象从而控制对它的访问,其中主要方法是将方位聚集为租或者 仅当真正必要时侯才执行访问,从未避免高昂的操作开销。
通过是你的对象之间相互不直接“通话”,而是通过一个中介者对子昂进行通信, 从而形成松散耦合。
通过创建“可观察”的对象,当发生一个感兴趣的事件时可将改时间通告给所有观察者 从而形成松散耦合。