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