jQuery源码阅读之jQuery.Callbacks ()

本文详细解析了jQuery.Callbacks的功能及实现原理,包括add、fire、remove等核心方法,并通过多个示例代码展示了不同参数配置下的行为差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天读了下《jquery 技术内幕》这本书 里边jquery.Callbacks()讲解的非常好,尤其是各种例子,这里贴出注释,和书里的测试代码,拿着测试代码对照着注释一步一步的调试Callbacks就能比较深刻的理解jQuery里回调函数列表的管理是怎么设计的。


代码注释:

//jQuery Callbacks 

	/*
		jQuery 的Callbaks 是jQuery根据不同的参数来管理回调函数列表的
		基础函数,他为jQuery中的$.ajax()和$.Deferred()函数提供基础支持,它
		提供add(),fire(),remove(),disabled()等功能
		参数 flags:空格分割的改变回调函数行为的参数
		默认情况下一个回调函数列表就像一组事件性质的回调函数列表
		可能的参数
		flags的可能值:
		once: 回调函数列表只触发一次
		memory:记录上一次回调函数参数,之后的回调函数添加就会立即使用记录的参数立刻调用
		unique: 保证一个回调函数只加入列表一次,不能有重复的函数
		stopOnFalse:打断回调函数列表 当回调函数返回 false
		jquery中 用了 once memoery的参数组合表示函数只触发一次,以后添加的函数会已记录的参数,立即执行
	*/
function createFlags( flags ){
	var object = flagsCache[ flags ] = {}, //注意这个技巧,object和flagsCache flagsCache[flags]指向同一个空的对象,为object添加属性就是为flagsCache[flags]添加属性
		i,length;
	flags = flags.split(/\s+/);
	for( i = 0,length = flags.length; i < length; i++){
		object[flags[i]] = true;
	}
	flagsCache[]
}

	jQuery.Callbacks = function(flags){
		

		//把字符串的参数 变为对象类型的参数 首先从缓存中去取

		flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {};

		var 
			list = [],  //回调函数列表
		    stack = [],  //当函数正在fire中,执行中时,把函数加入stack 等待执行
   		    memory,
   		    //是否正在触发中
   		    firing = false, 
   		    //开始fire的函数下标
   		    firingStart, 
   		    //需要fire函数列表的长度
   		    firingLength,
   		    //正在fire函数的下标
   		    firingIndex,
			
			add = function(args){
			var i,
				length,
				elem,
				type;
			for(i = 0,length = args.length; i < length){
				elem = args[i];
				type = jQuery.type(elem);
				
				if(type === "array"){
					//处理递归
					add(elem);
				}else if(type === "function"){
					 //注意这里的这个技巧 如果是unique模式,就去用has检查,用|| 加 ! 而不是用if else
					if(!flags.unique || !self.has(elem)){ 
						list.push(elem);
					}
				}
			}

		},
		//触发 callbacks
		fire = function(context,args){
			args = args || [];
			/*
				如果memory undefined 表示函数列表未被触发过 或者 已经被禁用
				在memory模式下 memory的值为[context,args] 间接表示回调函数列表被触发过
				非memory模式下 true 间接表示回调函数列表已经被触发过 once模式下不再触发
				stopOnFalse模式下 返回false 则memory为true 表示回调函数列表已经被处触发过
			*/
			memory = !flags.memory || [ context, args ]; 
			firing = true;
			firingIndex = firingStart || 0;
			firingStart = 0;
			firingLength = list.length;

			for(;list && firingIndex < firingLength; firingIndex++){
				if(list[firingIndex].apply(context,args) === false && flags.stopOnFalse){ //执行函数
					memory = true;
					break;
				}
			}
			firing = false; //触发结束
			//触发结束 
			if( list ){
				if(!flags.once){
					//对于在 上边for循环中【list[firingIndex].apply(context,args)】产生的fire 会在这里处理  
					if(stack && stack.length){
						memory = stack.shift(); //取数组
						self.firWith(memory[0],memory[1]);
					}
				}else if( memory === true){
					self.disable();
				}else{
					list = [];
				}
			}
		},

		// 返回的 Callbacks 对象
		self = {
			add:function(){
				if(list){
					var length = list.length;
					add(arguments);
					
					//哈哈,这里的注释是 我们需要把函数加到目前正在触发的队列么???
					if(firing){
						firingLength = list.length;
						//是 memory 模式 并且 还没有 stopOnFalse
					}else if(memory && memory !== true){
						firingStart = length;
						fire(memory[0],memory[1]); //立即执行 
					}

				}
				return this;
			},
			remove:function(){
				if(list){
					var args = arguments,
						argsIndex = 0,
						argsLength = arguments.length;

					for(; argsIndex < argsLength; argsIndex++){
						for(var i = 0; i < list.length; i++){
							if(args[argsIndex] === list[i]){
								//处理触发中的remove 改变firingIndex和firingLength
								if( firing ){
									if( i < firingLength ){
										firingLength--; //firingIndex 长度减一
										if( i <= firingIndex ){//目前执行中的函数为第2个,remove的是第4个跳过若remove的是第1个,firingIndex向前移动一个
											firingIndex--;
										}
									}
								}
								//从list列表中删除
								list.splice(i--,1);
								if(flags.unique){
									break
								}
							}
						}
					}
				}
				return this;
			},
			//判断函数是否已经在函数列表中
			has:function(fn){
				if(list){
					var i = 0,length = list.length;
					for(; i < length; i++){
						if(fn === list[i]){
							return true;
						}
					}
					return false;
				}
			},
			//彻底销毁回调函数列表
			disable: function(){
				list = stack = memory = undefined;
				return this;
			},
			//判断是否disable
			disabled:function(){
				return !list;
			},
			lock:function(){
				stack = undefined; //禁止了回调函数fire
				if( !memory || memory === true){
					self.disable();
				}
			}
			//从列表清除所有的回调函数
			empty:function(){
				list = [];
				return this;
			}
			//根据给定的上下文和参数来触发回调函数
			fireWith:function( context, args){ 
				if(firing){
					if(!flags.once){
						stack.push([context,args]); //如果正在函数执行过程中又产生触发 则会先存入stack
					}
				}else if( !(flags.once && memory)){
					fire(context,args);
				}
				return this;
			},
			fire:function(){
				self.fireWith(this,arguments);
				return this;
			},
			
		};

	}


测试代码:

<script>
/*
var callbacks = jQuery.Callbacks(),
something = true;
function fn1(args){
  console.log('fn1',args);
  if(something){
    callbacks.fire("测试:执行过程中,第二次触发");
    callbacks.fire("测试:执行过程中,第三次触发");
    something = false;
  }
}

callbacks.add(fn1);
callbacks.fire('测试:第1次触发'); //多次触发
*/



/*
var callbacks = jQuery.Callbacks('once');
function fn1(args){
  console.log('fn1',args);
}
function fn2(args){
  console.log('fn2',args);
}

callbacks.add(fn1);
callbacks.add(fn2);
callbacks.fire('测试once模式');
console.log('是否已被禁用?',callbacks.disabled());
*/

/*
var callbacks = jQuery.Callbacks('once stopOnFalse');
function fn1(args){
  console.log("fn1",args);
  return false;
}
function fn2(args){
  console.log('fn2',args);
}
callbacks.add(fn1);
callbacks.add(fn2);
callbacks.fire("测试:once+stopOnFalse模式");
console.log('是否被禁用?',callbacks.disabled());
*/

/*
var callbacks = jQuery.Callbacks('once memory');
function fn1(args){
  console.log('fn1',args);
}
function fn2(args){
    console.log('fn2',args);
}
callbacks.add(fn1);
callbacks.fire('测试 once模式 + memory 模式');
callbacks.add(fn2);
*/

/*
测试 add 中 if(fireing){  调整了 firingLength 会立即执行
*/
/*
var callbacks = jQuery.Callbacks();
function fn1(args){
  console.log('fn1',args);
  console.log('fn1 before add fn2');
  callbacks.add(fn2);
  console.log('fn1 after add fn2');
}
function fn2(args){
  console.log('fn2',args);
}

callbacks.add(fn1);
callbacks.fire("测试正在执行中 添加回调函数");
*/

/*
remove 的测试
*/
var callbacks = jQuery.Callbacks();
function fn1(args){
  console.log('fn1',args);
}
function fn2(args){
  console.log('fn2',args);
  console.log('before remove fn1');
  callbacks.remove(fn1);
  console.log('after remove fn1');
  console.log('before remove fn3');
  callbacks.remove(fn3);
  console.log('after remove fn3');
}
function fn3(args){
  console.log('fn3',args);
}
callbacks.add(fn1);
callbacks.add(fn2);
callbacks.add(fn3);
callbacks.fire('测试 移除毁掉函数,正在执行中,待移除函数已执行');
</script>



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值