看了下Sizzle部分源码,核心的原理就是使用正则表达式去匹配,找到对应的原生获取元素的方法,我没有去细究了。大家有兴趣可以自己看看,分享分享!
从2850行开始,继续往下读jQuery源码(2850-3043行)
进入Callbacks(回调函数管理模块)之前,有几个扩展方法
1.dir方法
三个参数:elem——dom元素,dir——指定elem的层级名称(例如parentNode,nextSibling),until——结束判断。返回一个数组,比如获取某个元素的parentNode,如果不指定until,则会返回一直到html的父级元素的数组。这里可以知道html的父级元素是document,document父级元素为null——没有父级元素。
2.pushStack:模拟入栈操作,生成了一个新的jQuery对象。
3.jQuery.Callbacks:回调函数的列表,管理函数模块
行:3066-3228
jQuery.Callbacks = function( options ) { // Convert options from String-formatted to Object-formatted if needed // (we check in cache first) options = typeof options === "string" ? ( optionsCache[ options ] || createOptions( options ) ) : jQuery.extend( {}, options ); var // Last fire value (for non-forgettable lists) memory, // Flag to know if list was already fired fired, // Flag to know if list is currently firing firing, // First callback to fire (used internally by add and fireWith) firingStart, // End of the loop when firing firingLength, // Index of currently firing callback (modified by remove if needed) firingIndex, // Actual callback list list = [], // Stack of fire calls for repeatable lists stack = !options.once && [], // Fire callbacks fire = function( data ) { memory = options.memory && data; fired = true; firingIndex = firingStart || 0; firingStart = 0; firingLength = list.length; firing = true; for ( ; list && firingIndex < firingLength; firingIndex++ ) { if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { memory = false; // To prevent further calls using add break; } } firing = false; if ( list ) { if ( stack ) { if ( stack.length ) { fire( stack.shift() ); } } else if ( memory ) { list = []; } else { self.disable(); } } }, // Actual Callbacks object self = { // Add a callback or a collection of callbacks to the list add: function() { if ( list ) { // First, we save the current length var start = list.length; (function add( args ) { jQuery.each( args, function( _, arg ) { var type = jQuery.type( arg ); if ( type === "function" ) { if ( !options.unique || !self.has( arg ) ) { list.push( arg ); } } else if ( arg && arg.length && type !== "string" ) { // Inspect recursively add( arg ); } }); })( arguments ); // Do we need to add the callbacks to the // current firing batch? if ( firing ) { firingLength = list.length; // With memory, if we're not firing then // we should call right away } else if ( memory ) { firingStart = start; fire( memory ); } } return this; }, // Remove a callback from the list remove: function() { if ( list ) { jQuery.each( arguments, function( _, arg ) { var index; while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { list.splice( index, 1 ); // Handle firing indexes if ( firing ) { if ( index <= firingLength ) { firingLength--; } if ( index <= firingIndex ) { firingIndex--; } } } }); } return this; }, // Check if a given callback is in the list. // If no argument is given, return whether or not list has callbacks attached. has: function( fn ) { return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); }, // Remove all callbacks from the list empty: function() { list = []; firingLength = 0; return this; }, // Have the list do nothing anymore disable: function() { list = stack = memory = undefined; return this; }, // Is it disabled? disabled: function() { return !list; }, // Lock the list in its current state lock: function() { stack = undefined; if ( !memory ) { self.disable(); } return this; }, // Is it locked? locked: function() { return !stack; }, // Call all callbacks with the given context and arguments fireWith: function( context, args ) { if ( list && ( !fired || stack ) ) { args = args || []; args = [ context, args.slice ? args.slice() : args ]; if ( firing ) { stack.push( args ); } else { fire( args ); } } return this; }, // Call all the callbacks with the given arguments fire: function() { self.fireWith( this, arguments ); return this; }, // To know if the callbacks have already been called at least once fired: function() { return !!fired; } }; return self; };
3.1 参数options,把注释翻译如下:
once:确保回调函数列表只触发一次,在fire上执行
memory:记录上一次触发的参数,之后添加的回调函数都使用该参数值被立即调用,在add上执行,添加的时候会判断是否执行
unique:同一个回调函数只能被添加一次,避免回调函数列表重复,在add上执行
stopOnFalse:当其中一个回调函数返回false时中断执行回调函数,在fire上执行
3.2 var rnotwhite=(/\S+/g/):匹配一个非空格字符。
这主要是将options字符串格式转换为对象格式并且缓存(optionsCache)
例如"once memory"通过createOptions("once memory")==>{"once":"true","memory":"true"}
3.3 主要包含的两个函数fire和self,Callbacks内部定义了一个list数组来存放回调函数列表
3.3.1 fire:触发所有回调函数执行,通过list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) 这个来执行函数,两个参数分别为:上下文和函数参数。
3.3.2 self:对回调函数list进行add,remove等操作
4.行3231-3461 Deferred和when(异步队列模块)
4.1 Deferred:相对于Callbacks,有3个函数列表,分别代表成功,失败,消息回调。
var tuples = [ // action, add listener, listener list, final state [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], [ "notify", "progress", jQuery.Callbacks("memory") ] ], ... jQuery.each( tuples, function( i, tuple ) { var list = tuple[ 2 ], stateString = tuple[ 3 ]; // promise[ done | fail | progress ] = list.add promise[ tuple[1] ] = list.add;
通过遍历tuples来生成了3个函数列表,并且以Add方法的形式添加到了promise里面。promise是一个异步队列只读的副本,只负责添加和判断,不更改状态和触发执行。在promise里面还定义了一个同名的promise方法,负责返回一个promise副本,then是一种快捷方式——在一个方法里面的添加3个函数列表(成功,失败,消息)。接着给promise添加了一个pipe属性指向then。在创建了deferred的只读副本以后,后面开始给deferred对象添加修改状态和触发执行的方法。
4.2 when:针对deferred的再一次封装,顾名思义,当所有的成功回调函数执行完毕时,然后再做什么事情。根据一个计数标志,每一次执行了成功回调函数以后就--1,当计算器==0的时候,就执行。如图:
4.3 最后会返回一个主异步队列对象return deferred.promise();
待续...