不得不说,jQuery现在的版本更新速度大有朝chrome看齐的趋势,看来chrome的版本更新价值观很有影响力,github上已经是 1.7版了,不过官方已发布依然是1.6.1,不过想来这些版本内核现在应该是很稳定了,所以学习的时候也不考虑那么多细节了,直接down最新版来啃!
在分析core的基本框架的时候已经弄清了,jQuery的实例对象是通过new init方法来返回的,那么init方法便是jQuery的核心方法了,也是jQuery的对外接口jQuery()(即$())的内部实现,熟悉 jQuery的童鞋应该知道除了在构建对象时封装DOM元素为类数组的jQuery实例对象,jQuery()方法也可以通过几种不同的调用方式达到不同 的效果,接下来看看源代码详细的分析:
init: function( selector, context, rootjQuery ) { var match, elem, ret, doc; // 分支1 在没有指定选择器的情况下,直接返回本身 // Handle $(""), $(null), or $(undefined) if ( !selector ) { return this; } // 分支2 ,当传递单个DOM元素时,直接将其作为jQuery数组的第一个元素 同样,其context也设置为该元素 // Handle $(DOMElement) if ( selector.nodeType ) { this.context = this[0] = selector; this.length = 1; return this; } // 分支3 因为body元素在一个页面只有一个 因此优化body 访问,context此时设置为document // The body element only exists once, optimize finding it if ( selector === "body" && !context && document.body ) { this.context = document; this[0] = document.body; this.selector = selector; this.length = 1; return this; } // 分支4 处理HTML字符串,此时又分成如下两种情况: // 1 : 形如<a></a>的html代码 // 2 : 形如#xxx的ID选择器 // Handle HTML strings if ( typeof selector === "string" ) { // 此处为一个优化,即直接判断第一个字符和最后一个字符是不是<>对已经总字符数大于3个, // 在这种条件下假设HTML字符串为正确的,因此略去了正则匹配 // Are we dealing with HTML string or an ID? if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { // Assume that strings that start and end with <> are HTML and skip the regex check match = [ null, selector, null ]; } else { // 此正则表达式用来匹配HTML字符串或者ID(#xxx)选择器 // 且该表达式并不完整验证字符串是否正确 // 注:quickExpr = /^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/ match = quickExpr.exec( selector ); } // 如果为形如"#xxx"的ID选择器,捕获组1则为undefined(即未匹配html字符串成功), // context的指定则会导致调用find方法 // Verify a match, and that no context was specified for #id if ( match && (match[1] || !context) ) { // 此时处理html代码 // HANDLE: $(html) -> $(array) if ( match[1] ) { context = context instanceof jQuery ? context[0] : context; doc = (context ? context.ownerDocument || context : document); // 处理单个标签情况, 形如 $("<div>") 或 $("<a></a>") // 注: rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/ // If a single string is passed in and it's a single tag // just do a createElement and skip the rest ret = rsingleTag.exec( selector ); if ( ret ) { if ( jQuery.isPlainObject( context ) ) { selector = [ document.createElement( ret[1] ) ]; // 现在attr方法和css方法在底层调用了access方法,此方法的分析先搁置一下, // 在创建HTML代码时,context用来为创建的对象设置其指定的属性 jQuery.fn.attr.call( selector, context, true ); } else { selector = [ doc.createElement( ret[1] ) ]; } } else { //非单个标签时将使用buildFragmenty来创建文档片段, // 该方法位于manipulation.js中,属于内部方法,以后分析 ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); //clone依然位于manipulation.js中,深度复制一个对象 // 此处将selector设置为文档片段的子节点 selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes; } //merge方法用于合并两个数组,在这里会将我们创建的文档片段或者元素合并到this中(即jQuery实例对象中) return jQuery.merge( this, selector ); // HANDLE: $("#id") } else { // 捕获组2是id值 elem = document.getElementById( match[2] ); // 一个bug,参见 http://bugs.jquery.com/ticket/6963 // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document #6963 if ( elem && elem.parentNode ) { // IE和opera返回name而不是ID,此时将调用find方法查找元素 // Handle the case where IE and Opera return items // by name instead of ID if ( elem.id !== match[2] ) { return rootjQuery.find( selector ); } // 否则直接将DOM元素插入到jQuery实例对象中 // Otherwise, we inject the element directly into the jQuery object this.length = 1; this[0] = elem; } this.context = document; this.selector = selector; return this; } // HANDLE: $(expr, $(...)) // 处理类似于$("div#div1",$("div#div2"))这种情况时,将递归查找,等价于$("divdiv2").find("div#div1"); } else if ( !context || context.jquery ) { return (context || rootjQuery).find( selector ); // 这种情况是我们处理时最普遍的情况,依然将调用find方法,在选择器代码中再做分析 // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { return this.constructor( context ).find( selector ); } // HANDLE: $(function) // Shortcut for document ready // 分支5 处理ready方法,$(fn)的情况 } else if ( jQuery.isFunction( selector ) ) { return rootjQuery.ready( selector ); } //防止$($("xxx"))这种情况处理时发生异常 if (selector.selector !== undefined) { this.selector = selector.selector; this.context = selector.context; } //将jQuery类数组转化为一个真正的数组([xx,xx]) return jQuery.makeArray( selector, this ); },
如代码中注释所言,jQuery对象就是这样一点点构建起来的,当然作为选择器的核心方法find留到后面再做分析,这次的分析先告一段落,下次将会看看jQuery是如何实现隐式迭代和链式调用的。