JS Hooks 介绍与使用
更新于:2023年4月4日
本页目录
致敬 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 backremove_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.");