jQuery源码学习笔记二
前文提要,jQuery用init方法创建的,它是jQuery.fn.init的实例而非jQuery的实例,后期会用jQuery.fn.init.
prototype = jQuery.fn; 把相应的能力从jQuery.prototype搬运到jQuery.fn.init.prototype上。因此,init之后,作者就放心地往 jQuery的原型添加方法。而这些原型更多的时候是往外调用其静态方法来工作。一开始,都是一些用于构建类数组的东西。像 setArray,makeArray,size,get,pushStack等方法。
?
//另一个复杂的方法? ? css: function( key, value ) {? ? ? ? // ignore negative width and height values? ? ? ? if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 )? ? ? ? ? ? value = undefined;? ? ? ? return this.attr( key, value, "curCSS" );? ? },? ?//与css与attr一样,既能读亦能写? ? text: function( text ) {? ? ? ? if ( typeof text !== "object" && text != null )? ? ? ? ? ? return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );?? ? ? ? var ret = "";?? ? ? ? jQuery.each( text || this, function(){? ? ? ? ? ? jQuery.each( this.childNodes, function(){? ? ? ? ? ? ? ? if ( this.nodeType != 8 )? ? ? ? ? ? ? ? ? ? ret += this.nodeType != 1 ?? ? ? ? ? ? ? ? ? ? ? ? this.nodeValue :? ? ? ? ? ? ? ? ? ? ? ? jQuery.fn.text( [ this ] );? ? ? ? ? ? });? ? ? ? });?? ? ? ? return ret;? ? },?? ? //把匹配的元素作出了为一个整体用参数里的标签(如果传入的是元素也会转换为标签)包起来,? ? //$("p").wrapAll('<div></div>') => ?<div><p>******</p><p>******</p><p>******</p></div>? ? //这东西应该叫wrapOutter更好? ? wrapAll: function( html ) {? ? ? ? if ( this[0] ) {? ? ? ? ? ? // The elements to wrap the target around? ? ? ? ? ? var wrap = jQuery( html, this[0].ownerDocument ).clone();?? ? ? ? ? ? if ( this[0].parentNode )? ? ? ? ? ? ? ? wrap.insertBefore( this[0] );?? ? ? ? ? ? wrap.map(function(){? ? ? ? ? ? ? ? var elem = this;?? ? ? ? ? ? ? ? while ( elem.firstChild )? ? ? ? ? ? ? ? ? ? elem = elem.firstChild;?? ? ? ? ? ? ? ? return elem;? ? ? ? ? ? }).append(this);? ? ? ? }?? ? ? ? return this;? ? },? ? //相当于把匹配的元素取得其innerHTML,然后用wrapAll方法包起来? ? //$("p").wrapInner('<div></div>') => ?<p><div>******</div></p><p><div>******</div></p><p><div>******</div></p>? ?wrapInner: function( html ) {? ? ? ? return this.each(function(){? ? ? ? ? ? jQuery( this ).contents().wrapAll( html );? ? ? ? });? ? },//与上面相反,有点像outterHTML,内容外面加一层皮//$("p").wrap('<div></div>') => ?<div><p>******</p></div><div><p>******</p></div><div><p>******</p></div>? ? wrap: function( html ) {? ? ? ? return this.each(function(){? ? ? ? ? ? jQuery( this ).wrapAll( html );? ? ? ? });? ? },
?
接着下来分析append,prepend,before,after。内部实现非常复杂,先扼要类比一下已有的API吧。
?
//append:向每个匹配的元素内部追加内容。//相当于//insertAdjacentHTML("beforeEnd",htmlstr)//insertAdjacentElement("beforeEnd",dom)//insertAdjacentElement("beforeEnd",jQueryEl)//并且具有处理复数个DOM对象的能力(内部用jQuery.each实现)//prepend:向每个匹配的元素内部前置内容。//相当于//insertAdjacentHTML("afterBegin",htmlstr)//insertAdjacentElement("afterBegin",dom)//insertAdjacentElement("afterBegin",jQueryEl)//并且具有处理复数个DOM对象的能力(内部用jQuery.each实现)//before:在每个匹配的元素之前插入内容。//相当于//insertAdjacentHTML("beforeBegin",htmlstr)//insertAdjacentElement("beforeBegin",dom)//insertAdjacentElement("beforeBegin",jQueryEl)//并且具有处理复数个DOM对象的能力(内部用jQuery.each实现)//after:在每个匹配的元素之后插入内容。。//相当于//insertAdjacentHTML("afterEnd",htmlstr)//insertAdjacentElement("afterEnd",dom)//insertAdjacentElement("afterEnd",jQueryEl)//并且具有处理复数个DOM对象的能力(内部用jQuery.each实现)
?
这些方法内部都调用一个叫domManip的方法,它的存在价值仅仅是为了兼容邪恶的火狐,因为火狐死活不支持IE的insertAdjacentXXX系列。
?
//主要用于返回上次覆盖了DOM元素数组end: function() {? return this.prevObject || jQuery( [] );},?// For internal use only.// Behaves like an Array's method, not like a jQuery method.push: [].push,sort: [].sort,splice: [].splice,//jQuery强大的CSS选择器find: function( selector ) {? if ( this.length === 1 ) {? ? var ret = this.pushStack( [], "find", selector );? ? ret.length = 0;? ? //分别为表达式,上下文,与之前获得的元素集合(它们将作为此次的搜索起点)? ? jQuery.find( selector, this[0], ret );? ? return ret;? } else {? ? //每次都会重新洗牌,因此必须进行压栈操作? ? return this.pushStack( jQuery.unique(jQuery.map(this, function(elem){? ? ? return jQuery.find( selector, elem );? ? })), "find", selector );? }},//先复制DOM再复制其上的事件clone: function( events ) {? // Do the clone? var ret = this.map(function(){? ? if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) {? ? ? // IE copies events bound via attachEvent when? ? ? // using cloneNode. Calling detachEvent on the? ? ? // clone will also remove the events from the orignal? ? ? // In order to get around this, we use innerHTML.? ? ? // Unfortunately, this means some modifications to? ? ? // attributes in IE that are actually only stored? ? ? // as properties will not be copied (such as the? ? ? // the name attribute on an input).? ? ? var html = this.outerHTML;? ? ? ?? ? ? if ( !html ) {? ? ? ? var div = this.ownerDocument.createElement("div");? ? ? ? div.appendChild( this.cloneNode(true) );? ? ? ? html = div.innerHTML;? ? ? }? ? ? //将字符串转换成jQuery对象? ? ? return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0];? ? } else? ? ? return this.cloneNode(true);? });? //许多是后面的方法,到时再说? // Copy the events from the original to the clone? if ( events === true ) {? ? var orig = this.find("*").andSelf(), i = 0;? ? ?? ? ret.find("*").andSelf().each(function(){? ? ? if ( this.nodeName !== orig[i].nodeName )? ? ? ? return;? ? ? ?? ? ? var events = jQuery.data( orig[i], "events" );? ? ? ?? ? ? for ( var type in events ) {? ? ? ? for ( var handler in events[ type ] ) {? ? ? ? ? jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );? ? ? ? }? ? ? }? ? ? ?? ? ? i++;? ? });? }? ?? // Return the cloned set? return ret;},//如果参数是函数则用jQuery.greg,否则用jQuery.multiFilerfilter: function( selector ) {? return this.pushStack(? jQuery.isFunction( selector ) &&? ? jQuery.grep(this, function(elem, i){? ? return selector.call( elem, i );? }) ||? ? ?? ? jQuery.multiFilter( selector, jQuery.grep(this, function(elem){? ? return elem.nodeType === 1;? }) ), "filter", selector );},//筛选最近的元素closest: function( selector ) {? //判断是否用于方位的? var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null,? closer = 0;? //把得到元素用map进行进一步的筛选? return this.map(function(){? ? var cur = this;? ? while ( cur && cur.ownerDocument ) {? ? ? if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) {? ? ? ? jQuery.data(cur, "closest", closer);? ? ? ? return cur;? ? ? }? ? ? cur = cur.parentNode;? ? ? closer++;? ? }? });},//用于反选,内部调用filternot: function( selector ) {? if ( typeof selector === "string" )? // test special case where just one selector is passed in? ? if ( isSimple.test( selector ) )//如果单一的类? ? ? return this.pushStack( jQuery.multiFilter( selector, this, true ), "not", selector );? else? ? selector = jQuery.multiFilter( selector, this );? //处理NodeList? var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType;? return this.filter(function() {//再回调用filter? ? return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector;? });},//添加新元素,内部进行清零压栈等操作add: function( selector ) {? return this.pushStack( jQuery.unique( jQuery.merge(? this.get(),? typeof selector === "string" ?? ? jQuery( selector ) :? ? jQuery.makeArray( selector ))));},//相当于javascript1.6 Array的some方法is: function( selector ) {? return !!selector && jQuery.multiFilter( selector, this ).length > 0;},//怎么这方法那么笨重?!hasClass: function( selector ) {? return !!selector && this.is( "." + selector );},//基本上是用于获取元素value属性的值//对于下拉开框,则是其innerText//与css,attr一样,可读可写val: function( value ) {? if ( value === undefined ) { ? ? ? ? ? ? ? var elem = this[0];? ? ?? ? if ( elem ) {? ? ? if( jQuery.nodeName( elem, 'option' ) )? ? ? ? return (elem.attributes.value || {}).specified ? elem.value : elem.text;? ? ? ?? ? ? // We need to handle select boxes special? ? ? if ( jQuery.nodeName( elem, "select" ) ) {? ? ? ? var index = elem.selectedIndex,? ? ? ? values = [],? ? ? ? options = elem.options,? ? ? ? one = elem.type == "select-one";? ? ? ? ?? ? ? ? // Nothing was selected? ? ? ? if ( index < 0 )? ? ? ? ? return null;? ? ? ? ?? ? ? ? // Loop through all the selected options? ? ? ? for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {? ? ? ? ? var option = options[ i ];? ? ? ? ? ?? ? ? ? ? if ( option.selected ) {? ? ? ? ? ? // Get the specifc value for the option? ? ? ? ? ? value = jQuery(option).val();? ? ? ? ? ? ?? ? ? ? ? ? // We don't need an array for one selects? ? ? ? ? ? if ( one )? ? ? ? ? ? ? return value;? ? ? ? ? ? ?? ? ? ? ? ? // Multi-Selects return an array? ? ? ? ? ? values.push( value );? ? ? ? ? }? ? ? ? }? ? ? ? ?? ? ? ? return values; ? ? ? ? ? ? ? ? ? ? }? ? ? ?? ? ? // Everything else, we just grab the value? ? ? return (elem.value || "").replace(/\r/g, "");? ? ? ?? ? }? ? ?? ? return undefined;? }? ?? if ( typeof value === "number" )? ? value += '';? ?? return this.each(function(){? ? if ( this.nodeType != 1 )? ? ? return;? ? //处理radio的checkbox checked属性,用于返回true与false? ? if ( jQuery.isArray(value) && /radio|checkbox/.test( this.type ) )? ? ? this.checked = (jQuery.inArray(this.value, value) >= 0 ||? ? ? jQuery.inArray(this.name, value) >= 0);? ? ?? ? else if ( jQuery.nodeName( this, "select" ) ) {? ? ? var values = jQuery.makeArray(value);? ? ? ?? ? ? jQuery( "option", this ).each(function(){? ? ? ? this.selected = (jQuery.inArray( this.value, values ) >= 0 ||? ? ? ? ? jQuery.inArray( this.text, values ) >= 0);? ? ? });? ? ? ?? ? ? if ( !values.length )? ? ? ? this.selectedIndex = -1;? ? ? ?? ? } else? ? ? this.value = value;? });},//就是innerHTMLhtml: function( value ) {? return value === undefined ?? ? (this[0] ?? ? this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g, "") :? ? null) :? ? this.empty().append( value );},//与replaceNode差不多replaceWith: function( value ) {? return this.after( value ).remove();},//把等于此索引值的DOM对象从jQuery对象中取出eq: function( i ) {? return this.slice( i, +i + 1 );},