在正式深入jQuery的核心功能选择器之前,还有一些方法,基本都是数组方法,用于遴选更具体的需要,如获得某个元素的所有祖选元素啦,等等。接着是其缓存机制data。
//去除两边的空白
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的性能很大部分依仗于它。
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 );
});
}
});