咱們就拿《head first 設計模式》中的一個例子入手,使用面向對象的編程思想:html
咱們有一家咖啡店,暫定每杯咖啡售價10元
function Coffee() { this.price = 10 } Coffee.prototype.getPrice = function () { return this.price } var coffee = new Coffee() console.log(coffee.getPrice());
如今新進了一批配料(摩卡,藍莓,奶泡)分別對應價錢爲(5,3,4),那麼咱們要怎麼計算一杯摩卡咖啡的價錢呢?
1.新建一個摩卡咖啡的對象:編程
function MochaCoffee() { this.price = 15 } MochaCoffee.prototype.getPrice = function () { return this.price } var mochaCoffee = new MochaCoffee() console.log(mochaCoffee.getPrice());
2.使用繼承設計模式
var coffee = new Coffee() function MochaCoffee() { this.mochaPrice = 5 } MochaCoffee.prototype = new Coffee() MochaCoffee.prototype.getPrice = function () { return this.price + this.mochaPrice } var mochaCoffee = new MochaCoffee() console.log(mochaCoffee.getPrice());
那麼每增長一種配料,就須要生成多一個類。app
可是,不管是第一種辦法,仍是第二種辦法都很難知足一下的狀況,好比函數
目前只有3種配料,咱們就須要添加3個新的類了,那麼當咱們的配料添加,或者添加(咖啡根據大中小杯去計算價錢)這種需求時,就須要添加無數的類去實現它;性能
若是隻是使用繼承,那麼當狀況複雜時,類的數量就會呈爆炸式增加,這是咱們所不但願見到的。this
使用構造器存儲coffee對象的引用prototype
function Mocha(coffee) { this.price = 5 this.coffee = coffee } Mocha.prototype.getPrice = function () { return this.price + this.coffee.getPrice() } var coffee = new Coffee() var mochaCoffee = new Mocha(coffee) console.log(mochaCoffee.getPrice());
若是這時咱們須要點一份雙倍摩卡的咖啡,直接以下調用:設計
var coffee = new Coffee() var mochaCoffee = new Mocha(coffee) var doubelMochaCoffee = new Mocha(mochaCoffee) console.log(doubelMochaCoffee.getPrice());
使構造器存儲coffee對象的引用一點也不靈活,咱們作以下改動:代理
function Mocha() { this.price = 5 } Mocha.prototype.getPrice = function () { return this.price + this.coffee.getPrice() } Mocha.prototype.setCoffee = function (coffee) { this.coffee = coffee } var coffee = new Coffee() var mochaCoffee = new Mocha() mochaCoffee.setCoffee(coffee) console.log(mochaCoffee.getPrice());
如今就能夠動態的設置coffee對象了!
若是你還想要一杯摩卡藍莓咖啡也是很是容易實現的:
function Blueberry() { this.price = 3 } Blueberry.prototype.getPrice = function () { return this.price + this.coffee.getPrice() } Blueberry.prototype.setCoffee = function (coffee) { this.coffee = coffee } var coffee = new Coffee() var mochaCoffee = new Mocha() mochaCoffee.setCoffee(coffee) var mochaBlueberryCoffee = new Blueberry() mochaBlueberryCoffee.setCoffee(mochaCoffee) console.log(mochaBlueberryCoffee.getPrice());
window.onload = function () { alert(1) } var _onload = window.onload window.onload = function () { _onload() alert('new') }
這是實際開發中很常見的一種作法,好比咱們想給 window 綁定 onload 事件,可是又不肯定 這個事件是否是已經被其餘人綁定過,爲了不覆蓋掉以前的 window.onload 函數中的行爲,我 們通常都會先保存好原先的 window.onload,把它放入新的 window.onload 裏執行,添加了一層簡單的包裝。
可是上面的作法會致使this丟失的問題,這裏不會報錯是由於全局變量就是window;
分別實現Function.prototype.before
和Function.prototype.after
,以下:
Function.prototype.before = function (beforefn) { var __self = this; // 保存原函數的引用 return function () { // 返回包含了原函數和新函數的"代理"函數 beforefn.apply(this, arguments); // 執行新函數,且保證 this 不被劫持,新函數接受的參數 // 也會被原封不動地傳入原函數,新函數在原函數以前執行 return __self.apply(this, arguments); // 執行原函數並返回原函數的執行結果, 2 // 而且保證 this 不被劫持 } } Function.prototype.after = function (afterfn) { var __self = this; return function () { var ret = __self.apply(this, arguments); afterfn.apply(this, arguments); return ret; } };
數據統計上報功能:
<html> <button tag="login" id="button">點擊打開登陸浮層</button> <script> var showLogin = function(){ console.log( '打開登陸浮層' ); log('上報信息'); } var log = function( tag ){ console.log( '上報信息爲: ' + tag ); } document.getElementById( 'button' ).onclick = showLogin; </script> </html>
咱們看到在 showLogin 函數裏,既要負責打開登陸浮層,又要負責數據上報,這是兩個層面 的功能,在此處卻被耦合在一個函數裏。使用 AOP 分離以後,代碼以下:
<html> <button tag="login" id="button">點擊打開登陸浮層</button> <script> Function.prototype.after = function( afterfn ){ var __self = this; return function(){ var ret = __self.apply( this, arguments ); 9 afterfn.apply( this, arguments ); return ret; } }; var showLogin = function(){ console.log( '打開登陸浮層' ); } var log = function(){ console.log( '上報信息爲: ' + 'balabalabla' ); } showLogin = showLogin.after( log ); // 打開登陸浮層以後上報數據 document.getElementById( 'button' ).onclick = showLogin; </script> </html>
在《設計模式》成書以前,GoF原想把裝飾者(decorator)模式稱爲包裝器(wrapper)模式。
其實裝飾者模式,就是在原有的基礎上添加了一層包裝,造成一條包裝鏈。
使用了裝飾者模式,咱們能夠動態的添加被裝飾者的職責,對它進行非入侵式的行爲修改,從而達到咱們要求而不須要建立更多的類。
缺點:當裝飾的層數過深時,實際上做用域鏈也就變的很長,性能也會受到影響。