javascript裝飾器模式,javascript裝飾模式
眾所周知裝飾器模式用於給對象在運行期間動態增加某個功能,職責等。相較通過繼承的方式來擴充對象的功能,裝飾器顯得更加靈活,首先,我們可以動態給對象選定某個裝飾器,而不用hardcore繼承對象來實現某個功能點。其次:繼承的方式可能會導致子類繁多,僅僅為了增加某一個單一的功能點,顯得有些多餘了。
裝飾器經典實現
首先來看看傳統的decorator的實現吧!假設現有一類叫Person 的對象,該對象有個speak 方法。開始的時候Person 的對象只會說中文,現在我要讓他說英文,那麼可以這麼做:
var Person = function(name) { this.name = name;};Person.prototype.speak = function() { console.log('你好!');};var SpeakEnglish = function(person) { this.person = person; };SpeakEnglish.prototype.speak = function() { this.speak(); console.log('hello!');};var SpeakJapanese = function(person) { this.person = person; };SpeakJapanese.prototype.speak = function() { this.speak(); console.log('こんにちは!');};var lufy = new Person('lufy');var engLufy = new SpeakEnglish(lufy);var japanLufy = new SpeakJapanese(engLufy);japanLufy.speak();
通過封裝對象的方式給原對象增加新的功能,而不會影響原對象的同類對象。通過上面的方式路飛現在可以說3個國家的語言了。
javascript裝飾函數
對於javascript這類支援函數式編程的語言,我們要裝飾對象的某個方法其實也可以更加簡便,譬如說,通常我們寫前端頁面處理的時候需要監聽視窗的onload 事件,但是同樣一個onload 需要被多個地方監聽,可以這麼做:
var attachOnloadHandle = function(doit) { if(typeof window.onload !== 'function') { window.onload = doit; } else { var func = window.onload; window.onload = function() { func(); doit(); }; }};
函數實現比較簡單,但也算實用吧!
裝飾器模式與AOP
AOP是面向切面編程的簡寫。其語意為把一些與核心業務模組無關的功能剝離出來,把整個系統劃分為更小的粒度。在完成這些子模組的設計編碼後再逐步封裝起來完成更大的功能。
javascript動態裝飾函數
通過前面的例子我們看到所謂裝飾無非是在執行某個功能之前執行一些前置或後置處理,提供兩個更加通用的介面:
Function.prototype.before = function(beforfunc) { var self = this; return function() { beforfunc.apply(this, arguments); self.apply(this, arguments); };};Function.prototype.after = function(afterfunc) { var self = this; return function() { self.apply(this, arguments); afterfunc.apply(this, arguments); };};
試一試:
var func = function() { console.log('hello'); };func.before(function() { console.log('你好');}).after(function() { console.log('こんにちは!');});
考慮到安全方面的因素,我們在給伺服器發送一些請求的時候都需要帶上accessToken 有些操作不需要帶這個參數,通過AOP函數封裝我們可以這樣來做:
var getToken = function(cb) { //... };var preRequest = function(doit, params) { var self = this; getToken(function(err, token) { if(err) { //xxx return; } self.token = token; doit.apply(self, params); });};var make = function(host, name, func) { host[name] = function() { this.preRequest(this['_' + name], arguments); }; host['_' + name] = func;};//擷取卡券make(exports, 'getCard', function(cardId, cb) { var url = 'http://www.wechat.com/xxxx' + this.token; //...});