致敬WordPress Hooks,開發了 Javascript 版的鉤子。不依托于任何第三方庫,可獨立運行。
什么是鉤子
鉤子分為?Action?和?Filter?兩種。
- Action:動作,執行到代碼這個位置時,同時調用其他方法。
- Filter:過濾器,代碼在這個位置可能需要過濾數據,數據通過參數傳遞給外部方法后返回。
無論WordPress還是Javascript,其目的都是:在代碼正常執行過程中為二次開發預留的接口。從而實現:一定程度上無需修改源代碼而實現二次開發。可以理解為:
- 編寫一套通用的工作流。工作流本身的編寫人提前考慮到一些可能的情況。并在考慮到的情況中預埋鉤子。
- 接下來,執行人在執行工作流前,預先添加工作流中可能用得到的鉤子,然后再執行工作流。當工作流執行到預埋鉤子的位置時,被添加的方法得到調用。
Action鉤子示例
有下面的工作流:
開始
-步驟1
-步驟2
-步驟3
-步驟4
-結束
在這個流程中,我們提前考慮到:當步驟2執行完成后,可能有其他的功能需要同時執行
。
但是我們不知道何時執行完步驟2。因此使用Action鉤子。
于是,工作流(源代碼)的編寫人(開發人員)在編寫源代碼時,在步驟2結束的代碼后,預埋了“do_action”鉤子,并告知了執行人(用戶)動作名稱。
最終源代碼發布給執行人(用戶)使用時,就可以通過“add_action”在步驟2執行完成后同時執行工作流以外的聯動方法。
Filter過濾器示例
有下面的工作流:
開始
-步驟1
-步驟2
-步驟3
-步驟4
-結束
在這個流程中,我們提前考慮到:步驟3的?數據可能會被再次處理
?后流轉到下一個步驟。
但是我們不知道步驟3的原始數據,也不知道何時執行完步驟3。因此使用Filter鉤子。
于是,工作流(源代碼)的編寫人(開發人員)在編寫源代碼時,在步驟3處理完后的數據中,預埋了“apply_filters”鉤子,并告知了執行人(用戶)過濾器名稱。
最終源代碼發布給執行人(用戶)使用時,就可以通過“add_filter”對步驟3的數據進行外部處理后返回,步驟4就能得到外部處理的結果。
類比WordPress鉤子
WordPress鉤子同樣是這個原理。例如:wp的the_content過濾器。在輸出文章數據的時候,如果發現用戶添加過the_content過濾器,就使用用戶過濾返回后的數據,于是輸出的文章內容就是用戶使用鉤子修改的內容了。
當然,用戶在添加這個鉤子的時候,并不知道文章內容是什么,所以也不知道如何修改。因此過濾器接收參數。用戶在過濾的時候就可以根據原始內容就行過濾。然后再返回。
方法名及參數
do_action
do_action(鉤子名稱:String, 參數1, 參數2…)
預埋鉤子,執行“add_action”的動作鉤子,并提供動作的額外參數。
add_action
add_action(鉤子名稱:String, 回調函數:Function, 優先級:Number)
添加鉤子動作,當代碼運行到“do_action”時,聯動同名的操作。聯動的回調參數接收“do_action”傳遞的參數。
- 默認優先級:10
- 邏輯上完全相同(===)的同一個鉤子,只會添加一次
例:在下面的工作流中,根據當前時間判斷是早上還是晚上。并調用不同的外部方法
//==========添加可用鉤子============
add_action('in_the_night',function(time){
console.log("It's " + time + ", I have to sleep.");
});
add_action('in_the_morning',function(time){
console.log("It's " + time + ", I have to go to school.");
});
//==========正常的代碼工作流============
var date = new Date(),
hour = date.getHours(),
minute = date.getHours(),
time = hour + ":" + minute;
if (hour >= 21 && hour <= 23) {
console.log('Good night!');
do_action('in_the_night', time);
}
if (hour >= 6 && hour <= 9) {
console.log('Good morning!');
do_action('in_the_morning', time);
}
//==========運行結果===========
// 在6點~9點間,屏幕輸出:
// Good morning!
// It's (當前時間), I have to go to school.
//
// 在21點~23點間,屏幕輸出:
// Good night!
// It's (當前時間), I have to sleep.
apply_filters
過濾結果 = apply_filters(鉤子名稱:String, 被過濾的參數, 額外參數1, 額外參數2…)
預埋過濾器,執行“add_filter”的過濾器,并提供被過濾的數據。
若添加的過濾器有多個,則被過濾的數據為上一個過濾器返回的數據。
add_filter
add_filter(鉤子名稱:String, 回調函數:Function, 優先級:Number)
添加鉤子過濾器,當代碼運行到“apply_filters”時,運行回調函數,在函數中并返回過濾后的參數。
- 必須返回參數,原則上返回的過濾后的參數應該與被過濾的參數類型、結構相同
- 默認優先級:10
- 邏輯上完全相同(===)的同一個鉤子,只會添加一次
例:有一個students的數組中包含每個學生的姓名、年齡、和自我介紹。讀取自我介紹時進行過濾:當學生沒有自我介紹時,自動根據名字和年齡生成自我介紹。
//==========添加可用鉤子============
add_action('introduce_oneself',function(introduce_original, name, age){
if (introduce_original == "") {
//沒有自我介紹,自動根據額外的參數(姓名和年齡)自動生成
return "My name is " + name + ", I am " + age + " years old.";
} else {
//有自我介紹,不作處理,直接返回
return introduce_original;
}
});
//==========正常的代碼工作流============
var students = [
{
name: "Mary",
age: 12,
introduce: ""
},
{
name: "Bob",
age: 13,
introduce: "I'm Bob, I like football."
}
];
//輸出自我介紹
for (var i = 0; i < students.length; i++) {
// 執行過濾器,給過濾器傳入被過濾的參數(introduce)、額外參數(name,age)
var introduce_filtered = apply_filters("introduce_oneself", students[i].introduce, students[i].name, students[i].age);
console.log(introduce_filtered);
}
//==========運行結果===========
// 屏幕輸出:
// My name is Mary, I am 12 years old.
// I'm Bob, I like football.
remove_action / remove_filter
remove_action(鉤子名稱:String, 回調函數:Function)
remove_filter(鉤子名稱:String, 回調函數:Function)
只有Function完全相同(===)時,才會被移除。與WordPress的PHP鉤子不同的是,無需提供優先級即可移除。
例:
//==========添加可用鉤子============
// Take some apple.
function take_apple(original_apple) {
return original_apple - 3;
}
add_filter('count_apple',take_apple);
// Put them back
remove_filter('count_apple',take_apple);
//==========正常的代碼工作流============
var original_apple = 5;
var new_apple = apply_filters("count_apple", original_apple);
console.log("There are " + new_apple + " apples.");