Jquery1.4.3源码分析(一)
Jquery是站在开发者的角度去考虑问题,在使用Js的时候,大部分时间都是对Dom元素进行操作,比如修改元素的属性,修改内容,修改CSS等。但是取Dom元素的,如getElementsByTag,有可能会取到一些Dom元素的集合,而又正好要这个集合的所有的元素都要进行同样的操作。如果只有一个元素,完全可以看作只有一个元素的集合。
这样只要对这个集合进行操作,就会对集合的每个元素都进行操作。jQuery就是基于这个集合而提供了众多的实用方法,包含了日常开发所需要的功能。对于这个集合,我们称为jquery对象。
我们可以通过$(params)或jquery(params)来生成Jquery对象。在Jquery文档中提供了四种方式:jQuery(expression,[context]),jQuery(html),jQuery(elements),jQuery(callback)四种构寻jquery对象的方式。其实Jquery的参数可以是任何的元素,如空参数,都能构成jquery对象。
那么jquery是如何实现的呢?
var jQuery = function( selector, context ) {//jQuery对象其实就是jQuery.fn.init函数生成的对象,不是通过new jQuery生成对象。return new jQuery.fn.init( selector, context );},
init: function( selector, context ) {var match, elem, ret, doc; //处理传空值的情况// Handle $(""), $(null), or $(undefined)if ( !selector ) {return this;} //// 第一种情况 Handle $(DOMElement)单个Dom 元素,忽略上下文// Handle $(DOMElement)if ( selector.nodeType ) {this.context = this[0] = selector;this.length = 1;return this;}//如果selector为'body',而且只存在一次,那么使用优化找到它// The body element only exists once, optimize finding itif ( selector === "body" && !context && document.body ) {this.context = document;this[0] = document.body;this.selector = "body";this.length = 1;return this;} //// Handle HTML stringsif ( typeof selector === "string" ) {// Are we dealing with HTML string or an ID?match = quickExpr.exec( selector );// Verify a match, and that no context was specified for #idif ( match && (match[1] || !context) ) { //处理$(html) -> $(array)// HANDLE: $(html) -> $(array)if ( match[1] ) {doc = (context ? context.ownerDocument || context : document); //如果只有个一个字符串传递进来,那么直接使用createElement创建元素,跳过剩下的部分// 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] ) ];jQuery.fn.attr.call( selector, context, true );} else {selector = [ doc.createElement( ret[1] ) ];}} else { //$('<div>sgsgsg</div>')解析字符串ret = jQuery.buildFragment( [ match[1] ], [ doc ] );selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes;}//把创建出来的element放进Jquery对象集合中.return jQuery.merge( this, selector );// HANDLE: $("#id") //处理$("#id")} else {elem = document.getElementById( match[2] );// Check parentNode to catch when Blackberry 4.6 returns// nodes that are no longer in the document #6963if ( elem && elem.parentNode ) {// Handle the case where IE and Opera return items// by name instead of IDif ( elem.id !== match[2] ) {return rootjQuery.find( selector );}// Otherwise, we inject the element directly into the jQuery objectthis.length = 1;this[0] = elem;}this.context = document;this.selector = selector;return this;}// HANDLE: $("TAG") 处理$('p')} else if ( !context && !rnonword.test( selector ) ) {this.selector = selector;this.context = document;selector = document.getElementsByTagName( selector );return jQuery.merge( this, selector );// HANDLE: $(expr, $(...)) 处理$(expr, [context])==$(content).find(expr)} else if ( !context || context.jquery ) {return (context || rootjQuery).find( selector );// HANDLE: $(expr, context)// (which is just equivalent to: $(context).find(expr)} else {return jQuery( context ).find( selector );}// HANDLE: $(function) 处理$(function)// Shortcut for document ready} else if ( jQuery.isFunction( selector ) ) {return rootjQuery.ready( selector );}if (selector.selector !== undefined) {this.selector = selector.selector;this.context = selector.context;} // 处理$(elements)return jQuery.makeArray( selector, this );}
jQuery.buildFragment = function( args, nodes, scripts ) {var fragment, cacheable, cacheresults,doc = (nodes && nodes[0] ? nodes[0].ownerDocument || nodes[0] : document); //只有缓存小于0.5K的字符串,有些元素不能缓存啊 // Only cache "small" (1/2 KB) strings that are associated with the main document// Cloning options loses the selected state, so don't cache them// IE 6 doesn't like it when you put <object> or <embed> elements in a fragment// Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cacheif ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document &&!rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {cacheable = true;cacheresults = jQuery.fragments[ args[0] ];if ( cacheresults ) {if ( cacheresults !== 1 ) {fragment = cacheresults;}}}if ( !fragment ) {fragment = doc.createDocumentFragment();jQuery.clean( args, doc, fragment, scripts );}if ( cacheable ) {jQuery.fragments[ args[0] ] = cacheresults ? fragment : 1;}return { fragment: fragment, cacheable: cacheable };};
// 把html转换成Dom元素,elems多个html string 的数组 clean: function( elems, context, fragment, scripts ) {context = context || document; ;//默认的上下文是document //在IE中!context.createElement行不通,因为它返回对象类型 //在IE中 typeof document.createElement返回object, firefox返回functionif ( typeof context.createElement === "undefined" ) { //这里支持context为jQuery对象,取第一个元素。context = context.ownerDocument || context[0] && context[0].ownerDocument || document;}var ret = [];for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {if ( typeof elem === "number" ) {elem += "";// 把int 转换成string的最高效的方法}if ( !elem ) {continue;}// Convert html string into DOM nodesif ( typeof elem === "string" && !rhtml.test( elem ) ) {elem = context.createTextNode( elem );//如果elem为文本,不包含元素标签} else if ( typeof elem === "string" ) {// Fix "XHTML"-style tags in all browsers //修改元素使得符合xhtml的标准elem = elem.replace(rxhtmlTag, "<$1></$2>");// Trim whitespace, otherwise indexOf won't work as expected //取得元素标签var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(), //判断是否要进行修正option, legend,thead,tr,td, col,areawrap = wrapMap[ tag ] || wrapMap._default,depth = wrap[0],div = context.createElement("div");// Go to html and back, then peel off extra wrappersdiv.innerHTML = wrap[1] + elem + wrap[2];// Move to the right depth 转到正确的深度,对于[1, "<table>","</table>"],div=<table>while ( depth-- ) {div = div.lastChild;}// fragments去掉IE对<table>自动插入的<tbody>if ( !jQuery.support.tbody ) { // 第一种情况:tags以<table>开头但没有<tbody>。在IE中生成的元素中可能会自动 // 加的<tbody> 第二种情况:thead|tbody|tfoot|colg|cap为tags, // 那wrap[1] == "<table>" .tbody不一定是tbody,也有可能是thead等等// String was a <table>, *may* have spurious <tbody>var hasBody = rtbody.test(elem),tbody = tag === "table" && !hasBody ?div.firstChild && div.firstChild.childNodes :// String was a bare <thead> or <tfoot>wrap[1] === "<table>" && !hasBody ?div.childNodes :[]; // 除去<tbody> for ( var j = tbody.length - 1; j >= 0 ; --j ) {if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {tbody[ j ].parentNode.removeChild( tbody[ j ] );}}} //使用innerHTML,IE会去开头的空格节点的,加上去掉的空格节点 // IE completely kills leading whitespace when innerHTML is usedif ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );}elem = div.childNodes;}if ( elem.nodeType ) {ret.push( elem ); //elem放进集合里面} else {ret = jQuery.merge( ret, elem );}}if ( fragment ) {for ( i = 0; ret[i]; i++ ) {if ( scripts && jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );} else {if ( ret[i].nodeType === 1 ) {ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );}fragment.appendChild( ret[i] );}}}return ret;},