jQuery源码学习(版本1.11)-queue

13 篇文章 0 订阅

概述

本文详细分析jquery-1.11.1.js源码文件行数:3957~4089;
代码简介:jQuery.queue里使用了data缓存的机制,为elem缓存一个回调队列,每次dequeue时,队列里回调出队的同时会执行,实现了一个回调机制,与Deferred那种相对独立的回调机制相比,这种回调是与elem紧紧关联的,回调函数跟elem绑定在一起;
下文进行详细代码分析。

代码分析

// 定义工具函数queue,dequeue,_queueHooks
jQuery.extend({
	// queue方法作用是建立一个数组保存函数,放在与elem相关的data里
	// 执行dequeue的时候,函数从数组里取出后会被执行,因此使用queue方法保存的必须是function,不能为其他类型数据
	queue: function( elem, type, data ) {
		var queue;

		if ( elem ) {
			// 传入的type作为队列名字,后缀再加个"queue",避免冲突
			type = ( type || "fx" ) + "queue";
			// 获取对应的数组缓存
			queue = jQuery._data( elem, type );

			// data不为空则说明是保存数据,为空是查看数据
			if ( data ) {
				// 两种场景进入if分支,一是前面获取到的queue为空,则jQuery.makeArray(data)会新建一个数组缓存在elem对应的cache里
				// 而是如果data是数组,则直接缓存进去,原queue存在的话会被替换掉
				if ( !queue || jQuery.isArray(data) ) {
					queue = jQuery._data( elem, type, jQuery.makeArray(data) );
				// else分支则是往已有数组push新数组
				} else {
					queue.push( data );
				}
			}
			return queue || [];
		}
	},

	dequeue: function( elem, type ) {
		type = type || "fx";

		// 获取对应数组队列
		var queue = jQuery.queue( elem, type ),
			// 队列长度
			startLength = queue.length,
			// 将queue第一个fn取出
			fn = queue.shift(),
			hooks = jQuery._queueHooks( elem, type ),
			// 定义一个next方法,内容是执行dequeue,next执行相当于递归
			next = function() {
				jQuery.dequeue( elem, type );
			};

		// 获取出来的fn是"inprogress"则继续获取
		// inprogress是为后面animate方法服务的标志,与其他场景使用队列无关
		if ( fn === "inprogress" ) {
			fn = queue.shift();
			// 队列长度减1
			startLength--;
		}

		if ( fn ) {

			// 后续分析animate再分析这里
			if ( type === "fx" ) {
				queue.unshift( "inprogress" );
			}

			// clear up the last queue stop function
			delete hooks.stop;
			// 执行被移除的fn,next作为参数传给fn,因此fn内部可以实现递归效果
			fn.call( elem, next, hooks );
		}

		// startLength为0则执行hooks.empty.fire()从缓存中删除掉队列
		if ( !startLength && hooks ) {
			hooks.empty.fire();
		}
	},

	// 队列勾子
	_queueHooks: function( elem, type ) {
		var key = type + "queueHooks";
		// 往缓存里增加一个勾子对象,对象里保存一个属性empty(Callbacks对象,用于删除queue和勾子自身)
		return jQuery._data( elem, key ) || jQuery._data( elem, key, {
			empty: jQuery.Callbacks("once memory").add(function() {
				jQuery._removeData( elem, type + "queue" );
				jQuery._removeData( elem, key );
			})
		});
	}
});

// 原型扩展queue相关方法
jQuery.fn.extend({
	// 实例的queue方法,用于缓存JQ对象的异步函数,出队的时候会被执行
	queue: function( type, data ) {
		// 表示参数数量,默认设为2
		var setter = 2;

		// 如果type不是字符串,则默认当做是data来用,而type变为默认值“fx”
		if ( typeof type !== "string" ) {
			data = type;
			type = "fx";
			// 原data废弃,因此setter减小1
			setter--;
		}

		// 假设形参长度比setter还小,说明是获取队列
		if ( arguments.length < setter ) {
			return jQuery.queue( this[0], type );
		}

		// data如果为undefined,前面返回队列的分支又没有进入,则在这里直接返回JQ对象
		return data === undefined ?
			this :
			// data为有效值,则调用each方法
			this.each(function() {
				// 回调函数里,对JQ对象的每一个元素,都调用工具queue保存好data
				// 注意这里的this跟外面调用each的不同,外面的this是指JQ对象,里面的this在回调被执行时,是JQ对象里的元素
				var queue = jQuery.queue( this, type, data );

				// 调用一下_queueHooks勾子方法,保证勾子方法内的内容也缓存了,这样在dequeue的时候就会执行勾子方法
				jQuery._queueHooks( this, type );

				// 这个应该跟后面的animate有关,后续再分析,其他情况的使用不会走进这个分支
				if ( type === "fx" && queue[0] !== "inprogress" ) {
					jQuery.dequeue( this, type );
				}
			});
	},
	
	// 实例的dequeue,很简单,直接使用each,回调里面使用工具dequeue
	dequeue: function( type ) {
		return this.each(function() {
			jQuery.dequeue( this, type );
		});
	},
	
	// 实例扩展了一个clearQueue,传空数组作为data达到清空queue效果
	clearQueue: function( type ) {
		return this.queue( type || "fx", [] );
	},
	
	// 返回defer.promise( obj ),obj存在就会将promise对象扩展进去,不存在就直接返回一个promise
	// 利用返回的promise可以增加异步回调,当dequeue操作把队列中所有的项都移除后,promise增加的回调函数就会执行
	promise: function( type, obj ) {
		var tmp,
			count = 1,
			defer = jQuery.Deferred(),
			// 引用JQ对象
			elements = this,
			// 获取JQ对象的长度
			i = this.length,
			// 定义resolve函数,里面计算并判断count决定是否执行defer.resolveWith
			resolve = function() {
				if ( !( --count ) ) {
					defer.resolveWith( elements, [ elements ] );
				}
			};

		if ( typeof type !== "string" ) {
			obj = type;
			type = undefined;
		}
		type = type || "fx";

		// 循环给JQ对象每一个里的每一个元素,获取其勾子对象,往里面添加resolve函数
		// 前面代码可知勾子对象里empty是一个Callbacks对象,会在dequeue时判断是否需要执行
		// 这样添加进去的resolve也会在dequeue移除完所有内容后被触发,promise的回调就会执行
		// 注意这里的queue,queueHooks是JQ对象里的每一个元素都各自有一份的(但里面缓存的回调函数,一般情况下使用实例方法添加的,都是相同的引用)
		// 与queue不同,promise回调只执行一次
		// 这里的count计数,是要保证JQ对象里的每一个元素都完成了所有出队动作,才会触发
		// 一般情况下如果只调用实例方法queue添加回调,count计数是多余的,因为实例方法会循环操作JQ对象全部元素,但是为了保证JQ对象里的一些元素queue里有其他回调,必须使用count
		while ( i-- ) {
			tmp = jQuery._data( elements[ i ], type + "queueHooks" );
			if ( tmp && tmp.empty ) {
				// 计数增加
				count++;
				tmp.empty.add( resolve );
			}
		}
		// 默认count = 1,这里需要先执行一次resolve
		resolve();
		return defer.promise( obj );
	}
});



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值