前言
單體模式
工廠模式
迭代器模式
裝飾者模式
策略模式
外觀模式
代理模式
中介者模式
觀察者模式
本文參考于《javascript模式》,因此會大量內容會和書中相同,手上有這本書的朋友可以直接看書。因為我的記憶習慣是抄書,所以我會先抄寫下來再發到博客上。
單體模式思想在于保證一個特定類僅有一個實例,意味著當你第二次使用同一個類創建信對象時,應得到和第一次創建對象完全相同。
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 |
instance 屬性暴露。
使用閉包
function Universe(){ var instance=this; //緩存this this.xx="xx"; Universe=function(){ //重寫此構造函數 return instance; } } var uni=new Universe(); var uni2=new Universe(); uni===uni2; //true |
因為重寫了構造函數,constructor 還是指向了老的構造函數,且實例化后在添加原型屬性也是不一樣的。如下
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 |
解決方法二問題。
function Universe(){ var instance; Universe=function Universe(){ return instance ; } Universe.prototype=this; //保存原型屬性 instance=new Universe(); instance.constructor=Universe; instance.xx="xx"; } |
自運行函數。
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 |
工廠模式是為了創建對象。
公共構造函數 CarMaker
名為factory的CarMaker靜態方法來創建car對象
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 |
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 |
Object() 構造函數即為內置工廠對象。
有一個包含某種數據集合的對象,該數據可能存儲在一個復雜數據結構內部,而要提供一個簡單方法訥訥感訪問到數據結構中沒一個元素。
next() 下一個
hasNext() 是否有下一個
reWind() 重置指針
current() 返回當前
var agg = (function() { var index = 0; var data = [1, 2, 3, 4, 5, 6]; var length = data.length; return { next: function() { //這里是從第一個數據開始 |
可以在運行時候添加附加功能到對象中,他的一個方便特征在于其預期行為的可定制和可配置特性。
例子 假設在開發一個銷售商品的Web應用,每一筆信銷售都是一個人新的 sale 對象。該對象“知道”有關項目的價格,并可以通過 getPrice() 方法返回加個。
根據不同情況,可以用額外的功能裝飾此對象。
假設客戶在魁北克省,買房需要支付聯邦稅和魁北克省稅,則此時需要調用聯邦稅裝飾者和魁北克省稅裝飾者。
var sale=new Sale(100); sale=sale.decorate("fedtax"); //聯邦稅 sale=sale.decorate("quebec"); //魁北克省稅 sale=sale.decorate("miney"); //轉為美元格式 sale.getPrice(); //返回價格 |
并且裝飾是可選的,例如不再魁北克省有可能沒有省稅。
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 |
此方法使用列表實現,而且相對來說比較好理解一點。本質就是把裝飾者名稱保存到一個列表中并且一次調用此列表中的方法。
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 |
策略模式支持在運行時候選擇算法。例如用在表單驗證問題上,可以創建一個具有 validate() 方法的驗證器對象,無論表單具體類型是什么,該方法都會被調用,
并且返回結果或者錯誤信息。
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")); } |
策略模式定義及例子實現參考與《javascript模式》及 湯姆大叔的博客
外觀模式即讓多個方法一起被調用
例如。 stopPropagation() 和 preventDefault() 兼容性一起調用。
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; } } } |
在代理模式中,一個對象充當另外一個對象的接口,和外觀模式區別是:外觀模式是合并調用多個方法。
代理模式是介于對象的客戶端和對象本身之間,并且對該對象的訪問進行保護。
現在有個包裹,賣家要把這個包裹寄給gary,則需要通過快遞公司寄過來,此時快遞公司就是一個 proxy
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你的包裹:鍵盤 |
本例子參考與 大熊君
權限列表
發帖 1
帖子審核 2
刪帖 3
留言、回復 4
用戶 | 代碼 | 權限 |
---|---|---|
注冊用戶 | 001 | 1 4 |
論壇管理員 | 002 | 2 3 4 |
系統管理員 | 003 | 1 2 3 4 |
游客 | 000 | null |
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() { //留言回復 }; |
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; } }; |
new Forum(new User("administartor","003")); |
中介者模式可以讓多個對象之間松耦合,并降低維護成本
例如:游戲程序,兩名玩家分別給與半分鐘時間來競爭決出勝負(誰按鍵的次數多勝出,這里玩家1按1,玩家2按0)
計分板(scoreboard)
中介者 (mediator)
中介者知道所有其他對象的信息。他與輸入設備(此時是鍵盤)進行通信并處理鍵盤上的按鍵時間,之后還將消息通知玩家。玩家玩游戲同時(每一分都更新自己分數)還要
通知中介者他所做的事情。中介者將更新后的分數傳達給計分板。
除了中介者莫有對象知道其他對象。
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); |
觀察者模式在 javascript 中使用非常廣泛。所有的瀏覽器時間就是該模式的實現,node.js中的events也是此模式實現。
此模式另一個名稱是 訂閱/發布模式
。
設計這種模式原因是促進形成松散耦合,在這種模式中,并不是一個對象調用另一個對象的方法,而是一個對象訂閱另一個對象的
特定活動并在狀態改編后獲得通知。訂閱者因此也成為觀察者,而被觀察的對象成為發布者或者主題。當發生了一個重要事件時候
發布者會通知(調用)所有訂閱者并且可能經常已事件對象的形式傳遞消息。
針對一個類僅創建一個對象。
根據字符串制定類型在運行時創建對象的方法。
提供一個API來遍歷或者操作復雜的自定義數據結構。
通過從預定義裝飾者對象中添加功能,從而在運行時侯調整對象
在懸在最佳策略以處理特定任務的時候仍然保持相同的接口。
通過把常用方法包裝到一個新方法中,從來提供一個更為便利的API。
通過包裝一個對象從而控制對它的訪問,其中主要方法是將方位聚集為租或者
僅當真正必要時侯才執行訪問,從未避免高昂的操作開銷。
通過是你的對象之間相互不直接“通話”,而是通過一個中介者對子昂進行通信,
從而形成松散耦合。
通過創建“可觀察”的對象,當發生一個感興趣的事件時可將改時間通告給所有觀察者
從而形成松散耦合。
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com