jQuery源码中定义了一些重要的静态属性和方法,它们是其他模块实现的基础,整体的源码结构如下
//把window.jQuery和winow.$备份到局部变量_jQuery和_$
_jQuery = window.jQuery,
// Map over the $ in case of overwrite
_$ = window.$,
jQuery.extend({
//许多 JavaScript 库使用 $ 作为函数或变量名,jQuery 也一样。在 jQuery 中,$ 仅仅是 jQuery 的别名,因此即使不使用 $ 也能保证所有功能性。假如我们需要使用 jQuery 之外的另一 JavaScript 库,我们可以通过调用 $.noConflict() 向该库返回控制权:
noConflict: function( deep ) {
//若window.$ ===jQuery,则设置window.$为初始化时备份的_$.
//只有在当前jQuery库持有全局变量$的情况下,才会释放$的控制器给前一个javascript库
if ( window.$ === jQuery ) {
window.$ = _$;
}
//当deep为true,且window.jQuery===jQuery,则设置window.jQuery为初始化时备份的_jQuery
if ( deep && window.jQuery === jQuery ) {
window.jQuery = _jQuery;
}
return jQuery;
},
// Is the DOM ready to be used? Set to true once it occurs.
isReady: false,
// A counter to track how many items to wait for before
// the ready event fires. See #6781
readyWait: 1,
// 用于判断传入的参数是否是函数
isFunction: function( obj ) {
return jQuery.type(obj) === "function";
},
isArray: Array.isArray || function( obj ) {
return jQuery.type(obj) === "array";
},
isWindow: function( obj ) {
return obj != null && obj == obj.window;
},
isNumeric: function( obj ) {
return !isNaN( parseFloat(obj) ) && isFinite( obj );
},
//用于判断参数的内建Javascript类型,如果参数是undefined或null,返回"undefined"或"null";如果参数是Js内部对象,则返回对应的字符串名称;其他情况一律返回"object"
type: function( obj ) {
return obj == null ?
String( obj ) :
//toString = Object.prototype.toString
//借用Object的原型方法toString()获取obj的字符串表示,返回值的形式为[object class],其中的class是内部对象类,Object.prototype.toString.call(new Date())返回[object Date]
class2type[ toString.call(obj) ] || "object";
},
//用于判断传入的参数是否是”纯粹“的对象,是否是对象直接量{}或 new Object()创建的对象
isPlainObject: function( obj ) {
//obj可以转换成false,Object.prototype.toString.call(obj)返回的不是[object object ],obj是DOM元素,obj是window对象,满足一个则返回false
if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
return false;
}
try {
// 检查对象obj是否由构造函数Object()创建,
对象obj含有属性constuctor,由 构造函数创建的对象都有一个constructor属性,默认引用了该对象的构造函数,如果对象obj没有属性constructor,则说明该对象必然是通过对象字面量{} 创建的
if ( obj.constructor &&
//hasOwn = Object.prototype.hasOwnProperty
!hasOwn.call(obj, "constructor") &&
!hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
return false;
}
} catch ( e ) {
// IE8,9 Will throw exceptions on certain host objects #9897
return false;
}
// 检查对象obj的属性是否都是非继承属性,如果没有属性,或者所有的属性都是非继承,怎返回true
var key;
for ( key in obj ) {}
return key === undefined || hasOwn.call( obj, key );
},
//用于检查对象是都是空的(不包含属性)
isEmptyObject: function( obj ) {
//for-in循环会同时枚举非继承属性和从原型对象继承的属性,如果有,则返回false,否则默认返回true
for ( var name in obj ) {
return false;
}
return true;
},
//接收一个字符串,抛出一个包含了该字符串的异常
error: function( msg ) {
throw new Error( msg );
},
//把一个json字符串,解析返回javascript对象,如果传入的残缺的json字符串可能导致抛出异常
parseJSON: function( data ) {
//如果不传入参数,或传入空字符串、null、undefined,则返回null
if ( typeof data !== "string" || !data ) {
return null;
}
// 移除开头和末尾的空白符,兼容IE6/7
data = jQuery.trim( data );
// 若支持,使用原生的方法JSON.parse()解析JSON字符串,并返回
if ( window.JSON && window.JSON.parse ) {
//JSON.parse()和JSON.stringify(),用于JSON字符串和javaScript对象之间的相互转换
//JSON.parse()解析JSON字符串为JSON对象,JSON.stringify()转换JSON对象为JSON字符串
return window.JSON.parse( data );
}
// 若不支持JSON.parse(),先检查字符串是否合法,
// rvalidchars = /^[\],:{}\s]*$/,
// rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
// rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
// rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g
if ( rvalidchars.test( data.replace( rvalidescape, "@" )
.replace( rvalidtokens, "]" )
.replace( rvalidbraces, "")) ) {
return ( new Function( "return " + data ) )();
}
jQuery.error( "Invalid JSON: " + data );
},
// 接受一个格式良好的xml字符传,返回解析后的XML文档
parseXML: function( data ) {
if ( typeof data !== "string" || !data ) {
return null;
}
var xml, tmp;
try {
if ( window.DOMParser ) {
//IE9+和其他标准浏览器
tmp = new DOMParser();
xml = tmp.parseFromString( data , "text/xml" );
} else {
// IE
xml = new ActiveXObject( "Microsoft.XMLDOM" );
xml.async = "false";
xml.loadXML( data );
}
} catch( e ) {
xml = undefined;
}
if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
jQuery.error( "Invalid XML: " + data );
}
return xml;
},
//空函数,开发插件的时候,可以作为回调函数的默认值,如果没有提供回调函数,则执行jQuery.noop()
noop: function() {},
// 用于在全局作用域中执行javaScript代码
globalEval: function( data ) {
if ( data && rnotwhite.test( data ) ) {
// execScript()在全局作用域中按照指定的脚本语言执行脚本代码,默认语言是Jscirpt,没有返回值
//如果支持方法execScript(),则执行execScript(data);否则创建一个自调用匿名函数,在函数内部执行
( window.execScript || function( data ) {
window[ "eval" ].call( window, data );
} )( data );
}
},
// 转换连接字符式的字符串为驼峰式,用于CSS模块和数据缓存模块
// rdashAlpha = /-([a-z]|[0-9])/ig,
// rmsPrefix = /^-ms-/,
camelCase: function( string ) {
//先用正则rmsPrefix匹配前缀"-ms-",如果有则修正为"ms-",然后用正则rdashAlpha匹配连字符"-"和其后的第一个字母或数字,并用字符串方法replace()和函数fcamelCase()把匹配部分替换为对应的大写字母或数字
return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
},
//用于检查DOM元素的节点名称,与指定的值是否相等,检查时忽略大小写
nodeName: function( elem, name ) {
//把属性elem.nodeName和参数name转换为大写再做比较,在执行elem.nodeName.toUpperCase()前先检测elem.nodeName是否存在,避免参数elem不是dom元素,或者elem没有属性nodeName导致错误
return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
},
// args is for internal usage only
each: function( object, callback, args ) {
//省略...
},
// 移除字符串开头和结尾的空白符
//rnotwhite = /\S/,
// trimLeft = /^\s+/,
// trimRight = /\s+$/,
// trim = String.prototype.trim
trim: trim ?
function( text ) {
//如果传入的参数是null或undefined,则返回空字符串
//支持String.prototype.trim()则借用此方法
return text == null ?
"" :
trim.call( text );
} :
// 不支持,则先调用方法toString()得到参数text的字符串表示,然后用正则trimLeft和trimRight匹配到的空白符替换为空字符串
function( text ) {
return text == null ?
"" :
text.toString().replace( trimLeft, "" ).replace( trimRight, "" );
},
// 将一个类数组对象转换成真正的数组
// array:待转换对象,可以是任何类型
//results:仅在jQuery内部使用,如果传入参数results,则在该参数上添加元素
makeArray: function( array, results ) {
var ret = results || []; //定义返回值
if ( array != null ) {
var type = jQuery.type( array );
if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) {
//array没有属性,array是字符串,array是函数,是window对象,是正则对象,满足一个则断定参数array不是数组
//插入元素时执行的是push.call(ret,array),这是因为返回值ret不一定是真正的数组。如果只传入参数array,则返回值ret是真正的数组,如果还传入的第二个参数ret,则返回值ret的类型取决于该参数的类型
push.call( ret, array );
} else {
//参数array是数组或类数组对象,调用jQuery.merge()把该参数合并到返回的值ret中
jQuery.merge( ret, array );
}
}
return ret;
},
//在数组中查找指定元素的并返回其下标
// elem: 要查找的值
//array:数组,将被遍历的数组,来查找参数value在其中的下标
//i: 指定开始查找的位置,默认是0
inArray: function( elem, array, i ) {
var len;
if ( array ) {
//如果支持数组方法indexOf(),则调用它并返回下标
if ( indexOf ) {
return indexOf.call( array, elem, i );
}
len = array.length;
//修正参数i,若没指定参数i,则初始化为0,如果小于0,则加上数组长度,即从数组末尾开始计算,Math.max()方法在0与len+i之间取最大值,如果len+i仍然小于0,则把i修正为0
i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
for ( ; i < len; i++ ) {
// Skip accessing in sparse arrays
if ( i in array && array[ i ] === elem ) {
return i;
}
}
}
return -1;
},
//合并两个数组到第一个数组,第一个数组将会被改变
//first: 数组或类数组对象,必须含有整型属性length
//second: 数组、类数组对象或任何含有连续整型属性的对象,其中的元素会被合并到第一个参数first中
merge: function( first, second ) {
var i = first.length,
j = 0;
//判断second的length是否是数值类型
if ( typeof second.length === "number" ) {
for ( var l = second.length; j < l; j++ ) {
first[ i++ ] = second[ j ];
}
} else {
//second没有属性length,则把该参数当作含有连续整型属性的对象,把其中的非undefined元素逐个插入参数first中
while ( second[j] !== undefined ) {
first[ i++ ] = second[ j++ ];
}
}
//手动修正first的length
first.length = i;
return first; //返回修改后的first数组
},
//用于查找数组中满足过滤函数的元素,原数组不会受影响
//array:待遍历的数组
//callback: 过滤每个元素的函数,执行时被传入两个参数:当前元素和它的下标,返回一个布尔值
grep: function( elems, callback, inv ) {
var ret = [], retVal;
inv = !!inv;
// 遍历数组,为每个元素执行过滤函数,如果inv为true,把执行结果false的元素放入结果数组ret中,
// that pass the validator function
for ( var i = 0, length = elems.length; i < length; i++ ) {
retVal = !!callback( elems[ i ], i );
if ( inv !== retVal ) {
ret.push( elems[ i ] );
}
}
return ret;
},
// arg is for internal usage only
map: function( elems, callback, arg ) {
//省略
},
// 全局计数器,用于jQuery事件模块和缓存模块,
guid: 1,
//接受一个函数返回一个新的函数,新函数总是持有特定的上下文
proxy: function( fn, context ) {
//如果参数context是字符串,说明参数格式是jQuery.proxy(context,name),修正为jQuery.proxy(fn,context)
if ( typeof context === "string" ) {
var tmp = fn[ context ];
context = fn;
fn = tmp;
}
// 如果fn不是函数,则返回undefined
if ( !jQuery.isFunction( fn ) ) {
return undefined;
}
// 收集多余参数,借用数组的slice()方法
var args = slice.call( arguments, 2 ),
proxy = function() {
return fn.apply( context, args.concat( slice.call( arguments ) ) );
};
// 为代理函数设置与原始函数相同的唯一标识guid,如果原始函数没有,则重新分配一个
proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
//返回创建的代码函数
return proxy;
},
//获取当前时间
now: function() {
return ( new Date() ).getTime();
},
// 浏览器嗅探
uaMatch: function( ua ) {
ua = ua.toLowerCase();
var match = rwebkit.exec( ua ) ||
ropera.exec( ua ) ||
rmsie.exec( ua ) ||
ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
[];
return { browser: match[1] || "", version: match[2] || "0" };
},
sub: function() {
function jQuerySub( selector, context ) {
return new jQuerySub.fn.init( selector, context );
}
jQuery.extend( true, jQuerySub, this );
jQuerySub.superclass = this;
jQuerySub.fn = jQuerySub.prototype = this();
jQuerySub.fn.constructor = jQuerySub;
jQuerySub.sub = this.sub;
jQuerySub.fn.init = function init( selector, context ) {
if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
context = jQuerySub( context );
}
return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
};
jQuerySub.fn.init.prototype = jQuerySub.fn;
var rootjQuerySub = jQuerySub(document);
return jQuerySub;
},
browser: {}
})
// 对象class2type
jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
class2type[ "[object " + name + "]" ] = name.toLowerCase();
});
函数fcamelCase ()负责把连字符后的字母转换成为大写并返回
fcamelCase = function( all, letter ) {
return ( letter + "" ).toUpperCase();
}