Callbacks:回调对象,函数的一个统一管理(观察者模式)
function test1() { console.log("test1 ..."); } function test2() { console.log("test2 ..."); } var cb = $.Callbacks(); cb.add(test1); cb.add(test2); cb.fire();
结果:json
test1 ...数组
test2 ...缓存
1.对不一样做用域的函数进行统一的管理app
2.简化统一调用函数
看下面的例子:this
var cb = $.Callbacks();//声明一个全局的回调对象 function test1() { console.log("test1 ..."); } cb.add(test1); (function () { function test2() {//test2为局部函数,外层中不能调用 console.log("test2 ..."); } cb.add(test2); })(); cb.fire()//简化了调用方式
结果:spa
test1 ...对象
test2 ...递归
参数说明:索引
1.once:使回调函数只能被调用一次,如:
cb.fire();
cb.fire();//第二次调用回调函数将无效
2.memory:无视添加回调函数的顺序,如:
cb.add(test1);
cb.fire();//test1/test2都将会执行
cb.add(test2);
3.unique:去除重复的回调函数,如:
cb.add(test1);
cb.add(test1);
cb.fire();//test1只会调用一次
4.stopOnFalse:当添加的回调函数有返回false时,后续的回调函数将不执行
cb.add(test1);//假设test1()为return false
cb.add(test2);
cb.fire();//将只会执行到test1,test2不会执行
5.此外,Callbacks还支持多个参数共同传递,如Callbacks('memory once')
参数处理:
var optionsCache = {};//options的缓存对象
function createOptions( options ) { var object = optionsCache[ options ] = {}; jQuery.each(//遍历options数组,进行处理 options.match( core_rnotwhite ) || [],//对options进行空格的切分 function( _, flag ) { object[ flag ] = true;//对单个option进行存放 } ); return object; }
options = typeof options === "string" ?//判断options是字符串 ( optionsCache[ options ]//读取缓存options || createOptions( options )//建立缓存对象 ) : jQuery.extend( {}, options );//options为空时,合并类数组,防止出现undefined
Callbacks('memory once')
1.options的状况:createOptions( options )返回的是{once:true, memory:true}
2.optionsCache的状况:optionsCache的值为{'once memory':{once:true, memory:true}}
3.options从新赋值:先根据options取optionsCache的值,为空则建立一个options
1.false状况:false null undefined 0 '' NaN
2.true状况:包括对象、数组、正则、函数等。注意 '0'、'null'、'false'、{}、[]也都是true
3.未定义的变量直接判断true/false程序报错
私有变量:
1.memeroy:最后一次触发回调时传的参数
2.fired:列表中的函数是否已经回调至少一次
3.firing:列表中的回调函数是否正在被回调中
4.firingStart:回调的起点
5.firingLength:须要fire的队列长度
6.firingIndex:当前正在firing的回调的队列的索引
7.list:[],定义回调函数列表
8.stack:!options.once&&[],当options有once属性时stack为[](主要用来保存当前正在执行回调函数时的函数,),不然为true
私有方法:在这里真正执行须要回调的函数
fire = function( data ) {//data= [context, [arg1, arg2, arg3, ...]],context=this memory = options.memory && data;//判断options是否有memory参数,无为undefined,不然为data fired = true; firingIndex = firingStart || 0;//赋值firingIndex firingStart = 0;//初始为0 firingLength = list.length;//回调列表长度 firing = true;//true表示回调函数正在执行 for ( ; list && firingIndex < firingLength; firingIndex++ ) {//遍历回调列表 // data[ 0 ]是函数执行的上下文,也就是平时的this // 这里看再看下 self.fireWith 传过来的参数 args 的格式 // 若是是stopOnFalse管理器,而且回调返回值是false,中断! // list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) 是最终的执行回调的方法 if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {//options中的stopOnFalse在这做用,执行带有false的回调函数而且有stopOnFalse参数,break出循环 memory = false; // To prevent further calls using add break; } } firing = false; if ( list ) { if ( stack ) { if ( stack.length ) { fire( stack.shift() ); } } else if ( memory ) {//options为stopOnFalse和memory的状况 list = []; } else { self.disable(); } } },
注意:建立Callbacks返回的是self,而self是一个json,存放Callbacks的公有方法
公有方法
1.add
// Actual callback list list = [],//回调函数列表
add: function() { if ( list ) {//list为空是,为ture // First, we save the current length var start = list.length;//保存回调函数列表的长度 (function add( args ) {//add自执行函数 jQuery.each( args, function( _, arg ) {//遍历参数,args=arguments var type = jQuery.type( arg );//判断参数的类型 if ( type === "function" ) { if ( !options.unique || !self.has( arg ) ) {//options中的unique在这做用,判断建立Callbacks是否有unique,取反,后判断回调函数是否在回调列表中 //1.options.unique为true,则为false,arg在list为true,反为false,不push进list中,反之 //2.options.unique为undefined ,则为true,不判断唯一性,直接push list.push( arg ); } } else if ( arg && arg.length && type !== "string" ) {//cb.add([test2,test1])这种状况 // Inspect recursively add( arg );//进行递归调用 } }); })( arguments );//将arguments传进add // Do we need to add the callbacks to the // current firing batch? if ( firing ) {//若是当前在 firing 当中,那就把须要firing的长度设置成列表长度 firingLength = list.length; // With memory, if we're not firing then // we should call right away } else if ( memory ) {//options中的memory在这做用,add后在次调用了fire //初始memory为空,则为false,当进行一次fire后,memory判断为true,调用fire,执行回调函数 firingStart = start; fire( memory ); } } return this; },
3.fire():执行回调列表中的回调方法,调用了另外一个公有方法,并传递this和参数列表
fire: function() {//调用回调函数 self.fireWith( this, arguments );//支持cb.fire('hello'); return this; },
4.fireWith():带有参数的调用私有的fire()
// 以给定的上下文和参数调用全部回调函数 fireWith: function( context, args ) { if ( list && ( !fired || stack ) ) {//options中的once在这做用,有once时,stack为false,当fire一次后,fired为true,因此第二次fire不执行 args = args || []; args = [ context, args.slice ? args.slice() : args ]; // 把 args 组织成 [context, [arg1, arg2, arg3, ...]] // function aaa() { // console.log(111) // cb.fire(); // } // function bbb() { // console.log(222) // } // var cb = $.Callbacks(); // cb.add(aaa); // cb.add(bbb); // cb.fire();这种状况 if ( firing ) {// 若是当前还在 firing, stack.push( args ); // 将参数推入堆栈,等待当前回调结束再调用 } else { fire( args );//args包含this和arguments } } return this; },
5.empty()
empty: function() {//清空回调列表 list = []; firingLength = 0; return this; },
6.disable()
// 禁用回调列表中的回调 // 禁用掉以后,把里边的队列、栈等所有清空了!没法再恢复了,再次调用fire()无效 disable: function() { list = stack = memory = undefined; return this; },
7.disabled():判断是否禁止,当调用disable()后,list为undefined,取反
disabled: function() { return !list; },
8.lock()
lock: function() {//锁住数组,禁止第二次fire(), stack = undefined;//清空栈,fire()一次后fired=true,fireWith()中( !fired || stack ) 为false if ( !memory ) { self.disable(); } return this; },
9.locked():lock后stack为undefined,取反,当options中有once参数,后续lock操做没意义
locked: function() {//list是否被锁住,返回一个锁标志 return !stack; },
10.remove():删除回调列表,支持多个回调函数一块儿删除
remove: function() {//删除回调列表 if ( list ) { jQuery.each( arguments, function( _, arg ) {//遍历参数列表 var index; while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { // splice(index,howmany) 方法向/从数组中添加/删除项目,而后返回被删除的项目 // index -- 必需。整数,规定添加/删除项目的位置 // howmany -- 必需。要删除的项目数量。若是设置为 0,则不会删除项目 // 从回调队列中移除当前查找到的这个方法 list.splice( index, 1 ); // Handle firing indexes // 在函数列表处于firing状态时,最主要的就是维护firingLength和firgingIndex这两个值 // 保证fire时函数列表中的函数可以被正确执行,防止取到(fire中的for循环须要这两个值 // function aaa() { // console.log(1); // cb.remove(bbb); // } if ( firing ) { if ( index <= firingLength ) { firingLength--; } if ( index <= firingIndex ) { firingIndex--; } } } }); } return this; },