在正式深入jQuery的核心功能选择器之前,还有一些方法,基本都是数组方法,用于遴选更具体的需求,如获得某个元素的所有祖选元素啦,等等。接着是其缓存机制data。
//@author 司徒正美|なさみ|cheng http://www.cnblogs.com/rubylouvre/ All rights reserved //去除两边的空白 trim: function( text ) { return (text || "").replace( /^\s+|\s+$/g, "" ); }, //转换成数组,很大众的方法 makeArray: function( array ) { var ret = []; if( array != null ){ var i = array.length; // The window, strings (and functions) also have 'length' if( i == null || typeof array === "string" || jQuery.isFunction(array) || array.setInterval ) ret[0] = array;//就只有一元素 else while( i )//处理数组 ret[--i] = array[i]; } return ret; }, //判断是否在数组中,类似indexOf inArray: function( elem, array ) { for ( var i = 0, length = array.length; i < length; i++ ) // Use === because on IE, window == document if ( array[ i ] === elem ) return i; return -1; }, //把新元素或第二个数组加入第一个数组中 //类似数组的concat merge: function( first, second ) { // We have to loop this way because IE & Opera overwrite the length // expando of getElementsByTagName var i = 0, elem, pos = first.length; // Also, we need to make sure that the correct elements are being returned // (IE returns comment nodes in a '*' query) if ( !jQuery.support.getAll ) { while ( (elem = second[ i++ ]) != null ) if ( elem.nodeType != 8 ) first[ pos++ ] = elem; } else while ( (elem = second[ i++ ]) != null ) first[ pos++ ] = elem; return first; }, //过滤重复元素,用done这个普通对象做过滤器(因为键如果同名将被覆盖掉) unique: function( array ) { var ret = [], done = {}; try { for ( var i = 0, length = array.length; i < length; i++ ) { var id = jQuery.data( array[ i ] ); if ( !done[ id ] ) { done[ id ] = true; ret.push( array[ i ] ); } } } catch( e ) { ret = array; } return ret; }, //类似数组的filter,这方法起得真不好,通常这都是与正则有关的…… //$.grep( [0,1,2], function(n,i){ // return n > 0; //}); //[1, 2] grep: function( elems, callback, inv ) { var ret = []; // Go through the array, only saving the items // that pass the validator function //写法很特别,callback之前的!是为了防止回调函数没有返回值 //javascript默认没有返回值的函数都返回undefined,这样一搞 //就变成true,原来返回true的变成false,我们需要负负得正,中和一下 //于是!=出场了,而inv也是未必存在的,用!强制转换成布尔 for ( var i = 0, length = elems.length; i < length; i++ ) if ( !inv != !callback( elems[ i ], i ) ) ret.push( elems[ i ] ); return ret; }, //就是数组中的map map: function( elems, callback ) { var ret = []; // Go through the array, translating each of the items to their // new value (or values). for ( var i = 0, length = elems.length; i < length; i++ ) { var value = callback( elems[ i ], i ); if ( value != null ) ret[ ret.length ] = value; } return ret.concat.apply( [], ret ); } }); // jQuery.browser下面的方法已经被废弃了,这些都是为兼容以前的版本与插件用 var userAgent = navigator.userAgent.toLowerCase(); // Figure out what browser is being used jQuery.browser = { version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1], safari: /webkit/.test( userAgent ), opera: /opera/.test( userAgent ), msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ), mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent ) }; //把以下方法parent,parents,next……添加到jQuery的原型上去,都是一些过滤方法 jQuery.each({ parent: function(elem){return elem.parentNode;}, parents: function(elem){return jQuery.dir(elem,"parentNode");}, next: function(elem){return jQuery.nth(elem,2,"nextSibling");}, prev: function(elem){return jQuery.nth(elem,2,"previousSibling");}, nextAll: function(elem){return jQuery.dir(elem,"nextSibling");}, prevAll: function(elem){return jQuery.dir(elem,"previousSibling");}, siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);}, children: function(elem){return jQuery.sibling(elem.firstChild);}, contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);} }, function(name, fn){ jQuery.fn[ name ] = function( selector ) {//方法体 var ret = jQuery.map( this, fn ); if ( selector && typeof selector == "string" ) ret = jQuery.multiFilter( selector, ret ); return this.pushStack( jQuery.unique( ret ), name, selector ); }; }); //把以下方法appendTo,prependTo,insertBefore……添加到jQuery的原型上去, //利用已有的append,prepend……方法构建 jQuery.each({ appendTo: "append", prependTo: "prepend", insertBefore: "before", insertAfter: "after", replaceAll: "replaceWith" }, function(name, original){ jQuery.fn[ name ] = function( selector ) { var ret = [], insert = jQuery( selector ); for ( var i = 0, l = insert.length; i < l; i++ ) { var elems = (i > 0 ? this.clone(true) : this).get(); jQuery.fn[ original ].apply( jQuery(insert[i]), elems ); ret = ret.concat( elems ); } return this.pushStack( ret, name, selector ); }; }); //一些重要常用的静态方法 jQuery.each({ removeAttr: function( name ) { jQuery.attr( this, name, "" ); if (this.nodeType == 1) this.removeAttribute( name ); }, addClass: function( classNames ) { jQuery.className.add( this, classNames ); }, removeClass: function( classNames ) { jQuery.className.remove( this, classNames ); }, toggleClass: function( classNames, state ) { if( typeof state !== "boolean" ) state = !jQuery.className.has( this, classNames ); jQuery.className[ state ? "add" : "remove" ]( this, classNames ); }, remove: function( selector ) { if ( !selector || jQuery.filter( selector, [ this ] ).length ) { // Prevent memory leaks jQuery( "*", this ).add([this]).each(function(){ jQuery.event.remove(this);//★★★★★ jQuery.removeData(this); }); if (this.parentNode) this.parentNode.removeChild( this ); } }, empty: function() { // Remove element nodes and prevent memory leaks jQuery(this).children().remove(); // Remove any remaining nodes while ( this.firstChild ) this.removeChild( this.firstChild ); } }, function(name, fn){ jQuery.fn[ name ] = function(){ return this.each( fn, arguments ); }; }); //将带单位的数值去掉单位 // Helper function used by the dimensions and offset modules function num(elem, prop) { return elem[0] && parseInt( jQuery.curCSS(elem[0], prop, true), 10 ) || 0; }
接着下来看jQuery的缓存机制,jQuery的性能很大部分依仗于它。
//@author 司徒正美|RestlessDream|なさみ|cheng http://www.cnblogs.com/rubylouvre/ All rights reserved var expando = "jQuery" + now(), uuid = 0, windowData = {}; jQuery.extend({ cache: {}, data: function( elem, name, data ) { //坚决不染指window elem = elem == window ? windowData : elem; //在elem上设置一个变量 var id = elem[ expando ]; // Compute a unique ID for the element if ( !id ) // 同时为id,elem[expando]赋值,值为单一数字 id = elem[ expando ] = ++uuid; // Only generate the data cache if we're // trying to access or manipulate it if ( name && !jQuery.cache[ id ] ) //在jQuery.cache上开辟一个对象,专门用于储存与那个elem有关的东西 jQuery.cache[ id ] = {}; // Prevent overriding the named cache with undefined values if ( data !== undefined )//data必须定义 jQuery.cache[ id ][ name ] = data; // Return the named cache data, or the ID for the element //根据第二个参数是否存在决定返回的是缓存数据还是element的特别ID return name ? jQuery.cache[ id ][ name ] : id; }, //移除缓存数据 removeData: function( elem, name ) { elem = elem == window ? windowData : elem; var id = elem[ expando ]; // If we want to remove a specific section of the element's data if ( name ) { if ( jQuery.cache[ id ] ) { // Remove the section of cache data delete jQuery.cache[ id ][ name ]; // If we've removed all the data, remove the element's cache name = ""; for ( name in jQuery.cache[ id ] ) break; if ( !name ) jQuery.removeData( elem ); } // Otherwise, we want to remove all of the element's data } else { // Clean up the element expando try { //IE不能直接用delete去移除,要用removeAttribute delete elem[ expando ]; } catch(e){ // IE has trouble directly removing the expando // but it's ok with using removeAttribute if ( elem.removeAttribute ) elem.removeAttribute( expando ); } // Completely remove the data cache //用缓存体中把其索引值也移掉 delete jQuery.cache[ id ]; } }, //缓存元素的类组数属性 //可读写 queue: function( elem, type, data ) { if ( elem ){ type = (type || "fx") + "queue"; var q = jQuery.data( elem, type ); if ( !q || jQuery.isArray(data) ) //q是数组 q = jQuery.data( elem, type, jQuery.makeArray(data) ); else if( data ) q.push( data ); } return q; }, //对元素的类数组缓存进行dequeue(也就是shift) dequeue: function( elem, type ){ var queue = jQuery.queue( elem, type ), fn = queue.shift(); if( !type || type === "fx" ) fn = queue[0]; if( fn !== undefined ) fn.call(elem); } }); //让jQuery对象也能获得这种缓存能力 //都是用上面静态方法实现,最终的缓存体还是jQuery.cache jQuery.fn.extend({ data: function( key, value ){ var parts = key.split("."); parts[1] = parts[1] ? "." + parts[1] : ""; if ( value === undefined ) { var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); if ( data === undefined && this.length ) data = jQuery.data( this[0], key ); return data === undefined && parts[1] ? this.data( parts[0] ) : data; } else return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){ jQuery.data( this, key, value ); }); }, removeData: function( key ){ return this.each(function(){ jQuery.removeData( this, key ); }); }, queue: function(type, data){ if ( typeof type !== "string" ) { data = type; type = "fx"; } if ( data === undefined ) return jQuery.queue( this[0], type ); return this.each(function(){ var queue = jQuery.queue( this, type, data ); if( type == "fx" && queue.length == 1 ) queue[0].call(this); }); }, dequeue: function(type){ return this.each(function(){ jQuery.dequeue( this, type ); }); } });