1. 簡介
AOP (Aspect Oriented Programming) ,意為:面向切面程式設計,通過預編譯方式和執行期動態代理實現程式功能的統一維護的一種技術。AOP是OOP的延續,是函數語言程式設計的一種衍生,利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。
2. 基礎實現
使用過java spring的同學一定知道,其內分為三種通知,before
(前置通知)、after
(後置通知)、around
(環繞通知)。
下面我們分別在js呼叫方法時實現這三種通知:
before(前置通知)
顧名思義,就是在函式呼叫前執行
Function.prototype.before = function (beforefun) {
var _orgin = this; // 儲存原函式引用
return function () { // 返回包含了原函式和新函式的"代理函式"
beforefun.apply(this, arguments); // 執行新函式,修正this
return _orgin.apply(this, arguments); // 執行原函式
}
};
var originFun = function(val){
console.log('原型函式: '+val);
}
var newFun = originFun.before(function(){
// 傳入函式呼叫前處理方法
console.log('before: ' + new Date().getTime())
})
newFun("測試前置通知");
// 呼叫結果
// before: 1557047939699
// 原型函式: 測試前置通知
複製程式碼
after(後置通知)
與before正相反,在函式呼叫後執行
Function.prototype.after = function (afterfun) {
var _orgin = this; // 儲存原函式引用
return function () { // 返回包含了原函式和新函式的"代理函式"
var ret = _orgin.apply(this, arguments); // 執行原函式
afterfun.apply(this, arguments); // 執行新函式,修正this
return ret;
}
};
var originFun = function(val){
console.log('原型函式: '+val);
}
var newFun = originFun.after(function(){
// 傳入函式呼叫前處理方法
console.log('after: ' + new Date().getTime())
})
newFun("測試後置通知");
// 呼叫結果
// 原型函式: 測試前置通知
// after: 1557047997647
複製程式碼
around(環繞通知)
在方法執行前後分別執行
// 利用前面的before、after方法實現
Function.prototype.around = function(beforeFun, afterFun) {
var _orgin = this;
return function() {
return _orgin.before(beforeFun).after(afterFun).apply(this, arguments);
}
}
複製程式碼
3. AOP遇到修飾器
JS在ES7的提案中終於增加了修飾器(Decorator)函式,它是用來修改類的行為,但是現在瀏覽器都不支援,需要使用Babel進行轉換,當AOP與修飾器結合後,又會給我們帶來什麼呢?
日誌記錄
通過AOP與修飾器的結合會很方便的進行日誌的記錄或者函式執行時間的記錄
class Person {
@log
say(nick) {
return `hi ${nick}`;
}
}
function log(target, name, decriptor){
var _origin = descriptor.value;
descriptor.value = function(){
console.log(`Calling ${name} with `, argumants);
return _origin.apply(null, arguments);
};
return descriptor;
}
var person = new Person();
person.say('小明');
複製程式碼
判斷使用者登入狀態
class User {
@checkLogin
getUserInfo() {
console.log('獲取使用者資訊')
}
}
// 檢查使用者是否登入
function checkLogin(target, name, descriptor) {
let method = descriptor.value
descriptor.value = function (...args) {
// 校驗方法,假設這裡可以獲取到使用者名稱/密碼
if (validate(args)) {
method.apply(this, args)
} else {
console.log('沒有登入,即將跳轉到登入頁面...')
}
}
}
let user = new User()
user.getUserInfo()
複製程式碼
4. React中的AOP
在react中使用AOP思想的典型就是高階元件(HOC),請看下面的例子
function HOCComp(WrappedComponent){
return class HOC extends Component {
render(){
const newProps = {param: 'HOC'};
return <div>
<WrappedComponent {...this.props} {...newProps}/>
</div>
}
}
}
@HOCComp
class OriginComponent extends Component {
render(){
return <div>這是原始元件{this.props.param}</div>
}
}
複製程式碼
上面例子中在HOCComp中定義新的props,並傳入子元件中。我們也可以對OriginComponent元件中的一些props進行加工,或對OriginComponent外層進行再次包裝。從而不必去修改內部元件,保持了功能上的解耦。
5. 總結
AOP思想在框架及專案中使用的很多,包括React高階元件、日誌記錄、登入驗證、redux中介軟體等。在開發中應該與OOP相輔相成,共同提高軟體的健壯性及可維護性。
參考資料