有時我們不希望某個類天生就非常龐大,一次性包含許多職責。那么我們就可以使用裝飾著模式。
裝飾著模式可以動態地給某個對象添加一些額外的職責,從而不影響這個類中派生的其他對象。
裝飾著模式將一個對象嵌入另一個對象之中,實際上相當于這個對象被另一個對象包裝起來,形成一條包裝鏈。
### 一、不改動原函數的情況下,給該函數添加些額外的功能
#### 1. 保存原引用
~~~
window.onload = function() {
console.log(1);
};
var _onload = window.onload || function() {};
window.onload = function() {
_onload();
console.log(2);
}
~~~
**問題**
(1)必須維護中間變量
(2)可能遇到this被劫持問題
在window.onload的例子中沒有這個煩惱,是因為調用普通函數_onload時,this也指向window,跟調用window.onload時一樣。
#### 2. this被劫持:
~~~
var _getElementById = document.getElementById;
document.getElementById = function(id) {
console.log(1);
return _getElementById(id);
}
return _getElementById(id); // 報錯“Uncaught TypeError: Illegal invocation”
~~~
因為_getElementById是全局函數,當調用全局函數時,this是指向window的,而document.getElementById中this預期指向document。
#### 3. 解決this被劫持:
~~~
var _getElementById = document.getElementById;
document.getElementById = function(id) {
console.log(1);
return _getElementById.call(document, id);
}
~~~
### 二、用AOP裝飾函數
~~~
/* 讓新添加的函數在原函數之前執行(前置裝飾)*/
Function.prototype.before = function(beforefn) {
var _self = this;
return function() {
beforefn.apply(this, arguments); // 新函數接收的參數會被原封不動的傳入原函數
return _self.apply(this, arguments);
};
};
~~~
~~~
/* 讓新添加的函數在原函數之后執行(后置裝飾)*/
Function.prototype.after = function(afterfn) {
var _self = this;
return function() {
var ret = _self.apply(this, arguments);
afterfn.apply(this, arguments);
return ret;
};
};
~~~
~~~
document.getElementById = document.getElementById.before(function() {
console.log(1);
});
~~~
### 三、避免污染原型
~~~
var before = function(fn, beforefn) {
return function() {
beforefn.apply(this, arguments);
return fn.apply(this, arguments);
};
};
var after = function(fn, afterfn) {
return function() {
var ret = fn.apply(this, arguments);
afterfn.apply(this, arguments);
return ret;
};
};
document.getElementById = before(document.getElementById, function(){
console.log(1);
});
~~~
### 四、示例–插件式的表單驗證
結合[《JavaScript設計模式–策略模式》](http://blog.csdn.net/ligang2585116/article/details/50365199)中的【表單驗證】,運用到ajax提交數據驗證,效果很棒!
修改上述before方法
~~~
var before = function(fn, beforefn) {
return function() {
if(beforefn.apply(this, arguments) === false) {
// beforefn返回false,直接return,不執行后面的原函數
return;
}
return fn.apply(this, arguments);
};
};
~~~
~~~
/* 模擬數據驗證*/
var validate = function() {
if(username === "") {
console.log("驗證失敗!");
return false;
}
return true;
}
/* 模擬ajax提交*/
var formSubmit = function() {
console.log("提交!!!");
}
username = 1;
formSubmit = before(formSubmit, validate); // 提交!!!
formSubmit();
username = "";
formSubmit = before(formSubmit, validate); // 驗證失敗!
formSubmit();
~~~
### 五、裝飾者模式和代理模式
**相同點**:
這兩種模式都描述了怎么為對象提供一定程度上的間接引用,它們的實現部分都保留了對另外一個對象的引用,并且向那個對象發送請求。
**區別:**
(1)代理模式:當直接訪問本地不方便或者不符合需求時,為這個本體提供一個替代者。本地定義關鍵功能,而代理提供或拒絕對它的訪問,或者在訪問本體之前走一些額外的事情。(其做的事情還是跟本體一樣)
(2)裝飾者模式:為對象動態加入行為。(一開始不能確定對象的全部功能,實實在在的為對象添加新的職責和行為)
轉載請標明出處:[http://blog.csdn.net/ligang2585116](http://blog.csdn.net/ligang2585116)