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

来源:互联网 发布:知楚王琦 编辑:程序博客网 时间:2024/05/22 06:34

概述

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

代码分析

// 定义工具函数queue,dequeue,_queueHooksjQuery.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();// 队列长度减1startLength--;}if ( fn ) {// 后续分析animate再分析这里if ( type === "fx" ) {queue.unshift( "inprogress" );}// clear up the last queue stop functiondelete 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 ) {// 表示参数数量,默认设为2var setter = 2;// 如果type不是字符串,则默认当做是data来用,而type变为默认值“fx”if ( typeof type !== "string" ) {data = type;type = "fx";// 原data废弃,因此setter减小1setter--;}// 假设形参长度比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,回调里面使用工具dequeuedequeue: 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.resolveWithresolve = 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里有其他回调,必须使用countwhile ( i-- ) {tmp = jQuery._data( elements[ i ], type + "queueHooks" );if ( tmp && tmp.empty ) {// 计数增加count++;tmp.empty.add( resolve );}}// 默认count = 1,这里需要先执行一次resolveresolve();return defer.promise( obj );}});



0 0
原创粉丝点击