Home >Web Front-end >JS Tutorial >A super detailed introduction to js design patterns
<br>
<br>
##Jan 14, 2017 |<br>
Table of Contents
<br>This article refers to "javascript mode", so a lot of the content will be the same as in the book. I have this book in hand. Friends who are book readers can read the book directly. Because my memory habit is to copy books, I will copy them down first and then post them on the blog.
Single Mode
Method 1
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; //true |
<br> |
<br> |
function Universe(){ var instance=this; //缓存this this.xx="xx"; Universe=function(){ //重写此构造函数 return instance; } } var uni=new Universe(); var uni2=new Universe(); uni===uni2; //true <br> |
因为重写了构造函数,constructor 还是指向了老的构造函数,且实例化后在添加原型属性也是不一样的。如下
<br> |
var uni = new Universe(); Universe.prototype.a = 1 var uni2 = new Universe(); console.log(uni === uni2) //true console.log(uni.a) //undefinded console.log(uni2.a) //undefinded console.log(uni.constructor === Universe); //false <br> |
解决方法二问题。
<br> <br> |
function Universe(){ var instance; Universe=function Universe(){ return instance ; } Universe.prototype=this; //保存原型属性 instance=new Universe(); instance.constructor=Universe; instance.xx="xx"; } <br> |
自运行函数。
<br> |
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) //true console.log(uni.a) //1 console.log(uni2.a) //1 console.log(uni.constructor === Universe); //true <br> |
工厂模式是为了创建对象。
公共构造函数 CarMaker
名为factory的CarMaker静态方法来创建car对象
<br> |
var corolla=CarMaker.factory('compact'); var solstice=CarMaker.factory('convertible'); var cherokee=CarMaker.factory('suv'); corolla.drive() //I have 4 doors solstice.drive() //I have 2 doors cherokee.drive() //I have 6 doors <br> |
<br> <br> |
<br> 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()); //I have 4 doors <br> |
Object() 构造函数即为内置工厂对象。
有一个包含某种数据集合的对象,该数据可能存储在一个复杂数据结构内部,而要提供一个简单方法讷讷感访问到数据结构中没一个元素。
next() 下一个
hasNext() 是否有下一个
reWind() 重置指针
current() 返回当前
<br> <br> |
var agg = (function() { var index = 0; var data = [1, 2, 3, 4, 5, 6]; var length = data.length; return { next: function() { //这里是从第一个数据开始输出 本例中为 1 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()); //1,2,3,4,5,6 } agg.reWind(); //此时重置指针到0 <br> |
可以在运行时候添加附加功能到对象中,他的一个方便特征在于其预期行为的可定制和可配置特性。
例子 假设在开发一个销售商品的Web应用,每一笔信销售都是一个人新的 sale 对象。该对象“知道”有关项目的价格,并可以通过 getPrice() 方法返回加个。<br>根据不同情况,可以用额外的功能装饰此对象。<br>假设客户在魁北克省,买房需要支付联邦税和魁北克省税,则此时需要调用联邦税装饰者和魁北克省税装饰者。
<br> |
var sale=new Sale(100); sale=sale.decorate("fedtax"); //联邦税 sale=sale.decorate("quebec"); //魁北克省税 sale=sale.decorate("miney"); //转为美元格式 sale.getPrice(); //返回价格 <br> |
并且装饰是可选的,例如不再魁北克省有可能没有省税。
<br> |
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; //对price进行处理 }, } Sale.decorators.quebec = { getPrice: function() { var price = this.uber.getPrice(); return price * 0.7; //对price进行处理 }, } Sale.decorators.money = { getPrice: function() { var price = this.uber.getPrice(); return "$" + price * 0.9; //对price进行处理 }, } /*decorate() 方法 调用装饰者方法 sale.=sale.decorate("fedtax"); fedtax字符串对应 Sale.decorators中的对象属性。新装饰对象 newobj 将继承目前我们所拥有的对象,这就是ixiangthis 为了完成继承部分代码,此时需要一个临时构造函数,先设置 newobj 的 uber 属性,以便于自对象可以访问到父对象。之后从装饰者中 将所有的额外属性复制到新装饰的对象 newobj 中,最后返回 newobj。 */ 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()); //$50.4 <br> |
此方法使用列表实现,而且相对来说比较好理解一点。本质就是把装饰者名称保存到一个列表中并且一次调用此列表中的方法。
<br> <br> |
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; //对price进行处理 }, } Sale.decorators.quebec = { getPrice: function(price) { var price = this.uber.getPrice(); return price * 0.7; //对price进行处理 }, } Sale.decorators.money = { getPrice: function(price) { var price = this.uber.getPrice(); return "$" + price * 0.9; //对price进行处理 }, } 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()); //$50.4 <br> |
策略模式支持在运行时候选择算法。例如用在表单验证问题上,可以创建一个具有 validate() 方法的验证器对象,无论表单具体类型是什么,该方法都会被调用,<br>并且返回结果或者错误信息。
<br> <br> |
var validator = { // 所有可以的验证规则处理类存放的地方,后面会单独定义 types: {}, // 验证类型所对应的错误消息 messages: [], // 当然需要使用的验证类型 config: {}, // 暴露的公开验证方法 // 传入的参数是 key => value对 validate: function (data) { var i, msg, type, checker, result_ok; // 清空所有的错误信息 this.messages = []; for (i in data) { if (data.hasOwnProperty(i)) { type = this.config[i]; // 根据key查询是否有存在的验证规则 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(); }, // helper hasErrors: function () { return this.messages.length !== 0; } }; //然后剩下的工作,就是定义types里存放的各种验证类了 // 验证给定的值是否不为空 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")); } <br> |
策略模式定义及例子实现参考与《javascript模式》及 汤姆大叔的博客
外观模式即让多个方法一起被调用
例如。 stopPropagation() 和 preventDefault() 兼容性一起调用。
<br> <br> |
var myEvent = { stop: function(e) { if (typeof e.preventDefault() === "function") { e.preventDefault(); } if (typeof e.stopPropagation() === "function") { e.stopPropagation(); } //for IE if (typeof e.returnValue === "boolean") { e.returnValue = false; } if (typeof e.cancelBubble === "boolean") { e.cancelBubble = true; } } } <br> |
在代理模式中,一个对象充当另外一个对象的接口,和外观模式区别是:外观模式是合并调用多个方法。<br>代理模式是介于对象的客户端和对象本身之间,并且对该对象的访问进行保护。
现在有个包裹,卖家要把这个包裹寄给gary,则需要通过快递公司寄过来,此时快递公司就是一个 proxy
<br> <br> |
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("键盘")); //gary你的包裹:键盘 <br> |
本例子参考与 大熊君
权限列表
发帖 1
帖子审核 2
删帖 3
留言、回复 4
用户 | 代码 | 权限 |
---|---|---|
注册用户 | 001 | 1 4 |
论坛管理员 | 002 | 2 3 4 |
系统管理员 | 003 | 1 2 3 4 |
游客 | 000 | null |
<br> <br> |
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() { //留言回复 }; <br> |
<br> <br> |
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; } }; <br> |
<br> <br> |
new Forum(new User("administartor","003")); <br> |
中介者模式可以让多个对象之间松耦合,并降低维护成本
例如:游戏程序,两名玩家分别给与半分钟时间来竞争决出胜负(谁按键的次数多胜出,这里玩家1按1,玩家2按0)
计分板(scoreboard)
中介者 (mediator)
中介者知道所有其他对象的信息。他与输入设备(此时是键盘)进行通信并处理键盘上的按键时间,之后还将消息通知玩家。玩家玩游戏同时(每一分都更新自己分数)还要<br>通知中介者他所做的事情。中介者将更新后的分数传达给计分板。
除了中介者莫有对象知道其他对象。
<br> <br> |
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) { //or keycode 对应按键 1 mediator.players.home.play(); return; } if (e.which === 48) { // 对应按键 0 mediator.player.guest.play(); return; } }, } //运行 mediator.setup(); window.onkeypress = mediator.keypress; setTimeout(function() { //设置30秒游戏时间 window.onkeypress = null; alert("game end"); }, 30000); <br> |
The observer pattern is very widely used in javascript. All browser time is the implementation of this mode, and events in node.js are also the implementation of this mode. <br>Another name for this mode is Subscription/Publishing Mode
. <br>The reason for designing this pattern is to promote the formation of loose coupling. In this pattern, it is not that one object calls the method of another object, but that one object subscribes to the <br>specific activity of another object and changes the state after Get notified. Subscribers therefore also become observers, and the observed objects become publishers or topics. When an important event occurs<br>The publisher will notify (call) all subscribers and may often deliver messages in the form of event objects.
Create only one object for a class.
A method to create objects at runtime based on a string specification type.
Provides an API to traverse or operate complex custom data structures.
Adjust objects at runtime by adding functionality from predefined decorator objects
Still maintaining the same interface while hanging on the best strategy to handle a specific task.
always provides a more convenient API by wrapping common methods into a new method.
Control access to an object by wrapping it. The main method is to aggregate the directions into rents or <br>Only perform access when really necessary. High operational overhead is never avoided.
Through this, your objects do not "talk" to each other directly, but communicate with each other through an intermediary, <br> thus forming loose coupling. .
By creating an "observable" object, when an event of interest occurs, the time can be notified to all observers<br>, thus forming loose coupling .
The above is the detailed content of A super detailed introduction to js design patterns. For more information, please follow other related articles on the PHP Chinese website!