功能描述
$.Callbacks作为jquery源码中的一个使用非常多的工具函数,为其他的许多模块提供支持.它的主要功能是使用一个add方法添加函数存储到函数队列中,使用fire方法去依次执行队列中的函数.另外我们可以添加各种参数对其执行队列中的函数的种种行为进行控制.下面我们先快速熟悉一下$.Callbacks的基本功能和用法.
var cb = $.Callback();
cb.add(function(){
conole.log("add one");
})
cb.add(function(){
conole.log("add two");
})
cb.fire() /*输出
add one
add two
*/
$.Callbacks()生成一个实例cb,随后调用add方法添加函数队列,使用fire方法执行函数队列中的函数.另外我们还可以在$.Callbacks生成实例的函数里添加各种参数,让其具备相应的特征.主要有下面4种参数:
1.once:函数队列只执行一次
2.unique:添加的重复函数保持唯一,不重复运行
3.topOnFalse :当某个函数的返回值是false ,停下来执行剩下的函数
4.memory:当函数队列fire一次后,内部会记录fire的参数,下次调用add的时候会将fire参数传递给新增的函数并且立即执行.如果fire函数添加过了参数,队列中的所有函数运行时都会接受这个参数
$.Callbacks源码的实现内容主要集中在上述4种参数影响下的函数体现的特征,如果想详细了解$.Callbacks具体用法可以参照jquery文档.
源码实现
在不考虑添加任何参数的情况下,$.Callbacks的实现逻辑十分简单.在用户调用add方法时将函数参数保存在list数组中,而当用户调用fire函数时遍历循环list数组并执行其中的函数就完成了.
once:如何保证函数队列只执行一次呢?添加一个fired变量来记录用户有没有触发过fire函数,如果第一次触发过了fire就置为true,下一次用户再调用fire函数时我们就可以根据fired变量的值和有没有传入"once"参数来决定要不要给他执行函数队列中的函数.
unique:unique的实现逻辑也很简单.在add方法中只需要判端list函数队列中有没有包含新加入的函数即可,再决定要不要把新加入的函数放入list队列中.
topOnFalse:要实现此参数的功能,那就需要在遍历执行函数队列的过程中得到函数的返回值,如果真为false再加上用户添加了"topOnFalse"约束那就需要使用break语句跳出for循环
memory:memory参数的功能稍微复杂一点,因为它要求用户执行完fire方法后,再使用add函数添加新函数时立即并只执行新函数,而不是从头到尾执行一遍函数队列.处理的关键点在于循环遍历list数组时起始索引值index的处理.
实现源码:
(function(root){
/**
* 生成配置
*/
function genConfig(params){
const array = params.split(/\s+/);
const object = {};
array.forEach((column)=>{
object[column] = true;
})
return object;
}
function callback(params){
const options = (typeof params === "string")?genConfig(params):{};
const list = [];
let i,fired,start_index;//fired用来记录是否已经使用fire函数触发过
let memory;
function fireHandler(context,parameter){
fired = true;
memory = options["memory"] && parameter;
i = start_index?start_index:0;
for(;i<list.length;i++){
if(list[i].apply(context,parameter) === false && options["topOnFalse"]){
break;
}
}
start_index = null;
}
const result = {
add:function(){
const fn_list = Array.prototype.slice.call(arguments);
fn_list.forEach((fn)=>{
if(toString.call(fn) === "[object Function]"){
if(!list.includes(fn) || !options["unique"]){
list.push(fn)
if(options["memory"] && fired){
start_index = list.length -1;
fireHandler(result,memory);
}
}
}
})
},
fire:function(){
if(!options["once"] || !fired){
const parameter = Array.prototype.slice.call(arguments);
fireHandler(result,parameter);
}
}
}
return result;
}
const $ = {
Callbacks:callback
}
root.$ = $;
})(window)
结果验证
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./lib/callbacks.js"></script>
</head>
<body>
</body>
<script>
var cb = $.Callbacks("memory once");
function fun(v){
console.log("add fun1:"+v);
return false;
}
cb.add(fun);
cb.add(function(v){
console.log("add fun2:"+v);
})
cb.fire("hello world11");
cb.add(function(v){
console.log("add fun3:"+v);
})
cb.add(function(v){
console.log("add fun4:"+v);
})
cb.fire("hello world22");
</script>
</html>
运行结果: