jQuery源代码阅读之二——jQuery静态属性和方法

重点掌握以下这些方法(可以手写代码):

1.深复制与浅复制

jQuery.extend

2.类型判断相关方法

jQuery.type,jQuery.isFunction,jQuery.isArray,jQuery.isWindow,jQuery.isNumberic,jQuery.isPlainObject,jQuery.isArrayLike

3.数组相关方法

jQuery.each,jQuery.map,jQuery.grep

4.转驼峰的实现

jQuery.camelCase

一、jQuery.extend/jQuery.fn.extend

    //可接受的参数类型如下:jQuery.extend([deep],target,object1,[objectN])
jQuery.extend = jQuery.fn.extend = function() {
var target = arguments[] || {}, //指向目标对象
deep = false, //是否进行深度复制
i = , //表示源对象的起始下标
length = arguments.length, //表示参数个数;
options, name, src, copy, copyIsArray; //options指向某个源对象,name指向源对象的某个属性名,src目标对象某个属性的原始值,copy某个源对象的某个属性的值,copyIsArray指示变量copy是否为数组
//首先进行参数修正
if (typeof target === 'boolean') {
deep = target;
target = arguments[] || {};
i = ;
}
//此时target就是jQuery或jQuery.fn
if (i === length) {
target = this;
i--;
}
//处理target是字符串或者其他情形,这在深度复制中可能出现
// if(typeof target!=='object'||!jQuery.isFunction(target)){
// target={};
// }
for (i; i < length; i++) {
options = arguments[i];
for (name in options) {
src = target[name];
copy = options[name];
if (deep && copy && (jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)))) {
if (copyIsArray) {
copyIsArray = false;
clone = src && jQuery.isArray(src) ? src : [];
} else {
clone = src && jQuery.isPlainObject(src) ? src : {};
}
//递归调用
target[name] = jQuery.extend(deep, clone, copy);
} else {
target[name] = copy;
}
}
}
return target;
};

jQuery.extend

二、jQuery静态方法和属性

大部分jQuery静态方法为实例方法提供底层支持,相关代码结构如下:

    jQuery.extend({
//一堆静态方法和属性
expando:'...',
noConflict:function(deep){},
isReady:false,
readyWait:1,
holdReady:function(hold){},
ready:function(){},
isFunction:function(obj){},
isArray:Array.isArray,
isWindow:function(obj){},
isNumeric:function(obj){},
type:function(obj){},
isPlainObject:function(obj){},
isEmptyObject:function(obj){},
error:function(msg){},
parseHTML: function( data, context, keepScripts ){},
parseJSON:JSON.parse,
parseCML:function(data){},
noop:function(){},
globalEval:function(data){},
camelCase:function(string){},
nodeName:function(elem,name){},
each:function(object,callback,args){},
trim:function(string){},
makeArray:function(array,results){},
inArray:function(elem,array,i){},
merge:function(first,second){},
grep:function(elems,callback,inv){},
map:function(elems,classback,arg){},
guid:1,
proxy:function(fn,context){},
access: function( elems, fn, key, value, chainable, emptyGet, raw ),
now:Date.now,
swap: function( elem, options, callback, args ),
});

下面逐一对上述方法的实现进行介绍,方法的作用,实现原理不少都在源代码的注释中,请展开来看。

2.1 jQuery.expando

expando:'jQuery'+(core_version+Math.random()).replace(/\D/g,''),

2.2 jQuery.noConflict

在变量区域定义:

        _jQuery=window.jQuery,
_$=window.$,
        noConflict:function(deep){
if(window.$===jQuery){
window.$=_$;
}
if(deep&&window.jQuery===jQuery){
window.jQuery=_jQuery;
}
return jQuery;
},

jQuery.noCoflict实现

2.3 (todo)ready事件相关静态方法

   JQuery.isReady,jQuery.readyWait;jQuery.holdReady;jQuery.ready 这几个静态方法与事件相关,暂且略过

2.4 类型判断相关静态方法,

  包括jQuery.isFunction,jQuery.isArray,jQuery.isWindow,jQuery.isNumeric,jQuery.type.这里方法的底层大部分都基于jQuery.type实现.

  具体实现如下

      /****下面是一系列类型检测的静态方法*******/
isFunction:function(obj){
//如果使用typeof,在有些浏览器中,正则也会返回function,因此这里采用jQuery处理后的方法,jQuery.type
return jQuery.type(obj)==='function';
},
isArray:Array.isArray,
isWindow:function(obj){
return obj!==null&&obj===obj.window;
},
//判断obj是否为数字或者数字类型的字符串,并且是有效数字
isNumeric:function(obj){
return !isNaN(parseFloat(obj))&&isFinite(obj);
},

类型判断相关方法

   // 每个对象都有一个toString()方法,当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用。默认情况下,toString()方法被每个Object对象继承。
  // 如果此方法在自定义对象中未被覆盖,toString() 返回 "[object type]",其中type是对象的类型。
  type:function(obj){
if(obj===null){
return String(null);
}
//Date,Array等类型typeof都会返回object,function、正则(部分浏览器)中 typeof都会返回function if(typeof obj==='object'||typeof obj==='function'){
return class2type[core_toString.call(obj)];
}
return typeof obj;
},

其中在变量区域定义class2type

        class2type={},

并为之赋值

    //目前,js中typeof的返回值有六种:"number," "string," "boolean," "object," "function," 和 "undefined."
//通过object.prototype.toString/或者{}.toString 返回值有九种:Boolean Number String Function Array Date RegExp Object Error,其中的Array,Date,RegExp,Object,Error都属于Object类型,在有些浏览器中typeof 正则会返回function
jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(i,name){
class2type["[object "+name+"]"]=name.toLowerCase();
});

2.4 jQuery对象判断相关方法

  jQuery.isPlainObject,jQuery.isEmptyObject

  jQuery.isPlainObject判断的原理,在于判断一些特定的方法,比如isPrototypeOf,hasOwnProperty,是否位于对象自身的原型,而非继承得到的原型链中

        isPlainObject:function(obj){
if(jQuery.type(obj)!=='object'||obj.nodeType||jQuery.isWindow(obj)){
return false;
} //如果是纯粹的对象,那么obj一定有constructor属性,并且方法hasOwnPropertyOf一定就在构造函数本身的原型中,而不用通过原型链查找得到
if(obj.constructor&&!core_hasOwn.call(obj.constructor.prototype,'isPrototypeOf')){
return false;
}
return true; },
//检查是否是空对象
isEmptyObject:function(obj){
for(var name in obj){
return false;
}
return true;
},

jQuery对象判断相关方法

2.5 jQuery数组遍历相关方法

  jQuery.each;jQuery.grep;jQuery.map

each:function(object,callback,args){
var i,
value,
length=object.length,
isArray=isArrayLike(object); if(args){//说明是内部调用
if(isArray){
for(i=;i<length;i++){
value= callback.call(object[i],args);
if(value===false){
break;
}
}
}else{
for(i in object){
value=callback.call(object[i],args);
if(value===false){
break;
}
}
}
}else{
if(isArray){
for(i=;i<length;i++){
value=callback.call(object[i],i,object[i]);
if(value===false){
break;
}
}
}else{
for(i in object){
value=callback.call(object[i],i,object[i]);
if(value===false){
break;
}
}
}
}
return object;
},
//用于查找数组中满足过滤函数的元素,形成新的数组之后返回,原数组不受影响
//如果inv未传入或者是false,元素只有在过滤函数返回true时,才会被保存在最终的结果数组中
//如果参数inv是true,则恰好相反
grep:function(elems,callback,inv){
var i,
ret=[],
length=elems.length,
retVal;
inv=!!inv;
for(i=;i<length;i++){
retVal=!!callback.call(elems[i],i);
if(retVal!==inv){
ret.push(elems[i]);
}
}
return ret;
},
//用于对数组中每个元素执行callback操作,并将结果形成新的数组返回
//参数arg仅仅是jQuery内部使用
map:function(elems,callback,arg){
var ret=[],
retVal,
i,
length=elems.length,
isArray=isArrayLike(elems);
if(isArray){
for(i=;i<length;i++){
retVal=callback.call(elems[i],i,arg);
if(retVal!=null){
ret.push(retVal);
}
}
}else{
for(i in elems){
retVal=callback.call(elems[i],i,arg);
if(retVal!=null){
ret.push(retVal);
}
}
}
//保证最终返回的是一维数组
return core_concat.call([],ret);
},

jQuery数组遍历相关方法

2.6 jQuery.error

        error:function(msg){
throw new Error(msg);
},

jQuery.error

2.7(todo) jQuery.parseHTML,jQuery.parseJSON,jQuery.parseXML

 parseHTML: function( data, context, keepScripts ){

        },
parseJSON:JSON.parse,
parseXML:function(data){
var xml, tmp;
if ( !data || typeof data !== "string" ) {
return null;
} // Support: IE9
try {
tmp = new DOMParser();
xml = tmp.parseFromString( data , "text/xml" );
} catch ( e ) {
xml = undefined;
} if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
jQuery.error( "Invalid XML: " + data );
}
return xml;
},

jQuery.parseHTML,jQuery.parseJSON,jQuery.parseXML

2.8 jQuery.noop,(todo)jQuery.globalEval,jQuery.camelCase,jQuery.nodeName

        noop:function(){},
//用于在全局作用域执行javascript代码,这里暂略
globalEval:function(data){},
//转换连字符字符串为驼峰类型
camelCase:function(string){
return string.replace(rmsPrefix,'ms-').replace(rdashAlpha,fcamelCase);
},
//判断elem的nodeName是否=name
nodeName:function(elem,name){
return elem.nodeName&&elem.nodeName.toLowerCase()==name.toLowerCase();
},

jQuery.noop/globalEval,camelCase,nodeName

2.9 jQuery.makeArray,jQuery.inArray

       makeArray:function(array,results){
var ret=results||[],
type=jQuery.type(array);
//undefined,null都会==null
if(array!=null){
//1,没有length属性,或者具有length属性,但是是以下几种情况的
//2.如果array是string 的length表示字符串的长度
//3.如果array是函数,其length代表函数生命时的参数个数
//4,如果array是window对象,属性Length返回窗口中的框架(frame,iframe)个数
if(array.length==null|| type=='string' || type=='function' ||type=='regexp'||jQuery.isWindow(array)){
core_push.call(ret,array);
}else{//否则说明是类数组对象
jQuery.merge(ret,array);
}
}
return ret;
}, inArray:function(elem,array,i){
return array==null?-:core_indexOf.call(array,elem,i);
},

jQuery.makeArray,/inArray

2.10 jQuery.merge

   //用于合并两个数组的元素到第一个数组中
//事实上,jquery源代码中第一个参数可以是数组或者类数组对象,第二个参数可以是数组、类数组对象或任何含有连续整型属性的对象
//第一个参数是数组,最后返回数组;第一个参数是类数组,则返回类数组
merge:function(first,second){
var l=second.length,
i=first.length,
j;
for(j=;j<l;j++){
first[i++]=second[j];
}
first.length=i;
return first;
},

jQuery.merge

2.11 jQuery.guid

    guid:,    

2.12 jQuery.proxy

        //该方法用于更改函数的执行上下文
//源代码中有两种传参形式,这里仅考虑最常见的一种
proxy:function(fn,context){
if(!jQuery.isFunction(fn)){
return undefined;
}
var args=core_slice.call(arguments,);
proxy=function(){
return fn.call(context||this,core_concat.call(args,core_slice.call(arguments)));
};
proxy.guid=fn.guid=fn.guid||jQuery.guid++;
return proxy;
},

2.13 (todo) jQuery.access

        //用一个方法同时实现get和set操作
//如何设置或者获取由回调函数fn确定
//这个方法的实现等用到的时候结合来看
access: function( elems, fn, key, value, chainable, emptyGet, raw ){ },

jQuery.access

2.14 jQuery.now,jQuery.swap

        now:Date.now,
//该方法用于交换css样式,在support模块较多用到
//要交换的样式由参数options传递
swap: function( elem, options, callback, args ){
var name,ret,
old={};
for(name in options){
old[name]=elem.style[name];
elem.style[name]=options[name];
}
ret=callback.call(elem,args||[]);
for(name in options){
elem.style[name]=old[name];
}
return ret;
},

jQuery.now/swap


截止目前,myJquey的当前版本代码:

(function(window,undefined){
var rootjQuery,
core_version='2.0.3',
idExpr=/^#([\w\-]*)$/,
//下面两个正则用于转驼峰
rmsPrefix = /^-ms-/,
rdashAlpha = /-([\da-z])/gi,
class2type={},
core_deletedIds=[], _jQuery=window.jQuery,
_$=window.$, core_toString=class2type.toString,
core_hasOwn=class2type.hasOwnProperty,
core_trim=core_version.trim,
core_indexOf=core_deletedIds.indexOf,
core_push=core_deletedIds.push,
core_concat=core_deletedIds.concat,
core_slice=core_deletedIds.slice,
//用于jQuery.camelCase转驼峰函数中
//当replace函数只有一个匹配项时,第二个参数可以是一个函数
//如果repalce中的正则没有捕获组,会向这个函数传递三个参数:模式的匹配项,模式匹配项在字符串中的位置,原始字符串
//如果replace中的正则有捕获组,也会向这个函数传递三个参数,模式的匹配项,捕获组的匹配项,模式匹配项在字符串中的位置
fcamelCase=function(all,letter){
return letter.toUpperCase();
},
jQuery=function(selector,context){
return jQuery.fn.init(selector,context,rootjQuery);
}; jQuery.fn=jQuery.prototype={
constructor:jQuery,
init:function(selector,context,rootjQuery){
var match,elem;
//selector是选择器表达式
if(typeof selector ==='string'){
match=idExpr.exec(selector);
if(match&&!context){
elem=document.getElementById(match[]);
if(elem&&elem.parentNode){
this[]=elem;
this.length=;
}
this.selector=selector;
this.context=document;
return this;
}else{
//说明是复杂的选择器表达式,这里暂不考虑
}
}
//处理selector是DOM元素的情形
if(selector&&selector.nodeType){
this[]=selector;
this.length=;
this.context=selector;
return this;
}
//处理selector是函数的情形
if(jQuery.isFunction(selector)){
return rootjQuery.ready( selector );
}
//处理selector是jQuery对象的情形
if(selector.selector){
this.selector=selector.selector;
this.context=selector.context;
}
//处理其他情形
return jQuery.makeArray(selector,this); }
}; jQuery.fn.init.prototype=jQuery.prototype; //可接受的参数类型如下:jQuery.extend([deep],target,object1,[objectN])
jQuery.extend=jQuery.fn.extend=function(){
var target=arguments[]||{},//指向目标对象
deep=false,//是否进行深度复制
i=,//表示源对象的起始下标
length=arguments.length,//表示参数个数;
options,name,src,copy,copyIsArray;//options指向某个源对象,name指向源对象的某个属性名,src目标对象某个属性的原始值,copy某个源对象的某个属性的值,copyIsArray指示变量copy是否为数组
//首先进行参数修正
if(typeof target==='boolean'){
deep=target;
target=arguments[]||{};
i=;
}
//此时target就是jQuery或jQuery.fn
if(i===length){
target=this;
i--;
}
//处理target是字符串或者其他情形,这在深度复制中可能出现
// if(typeof target!=='object'||!jQuery.isFunction(target)){
// target={};
// }
for(i;i<length;i++){
options=arguments[i];
for(name in options){
src=target[name];
copy=options[name];
if(deep&&copy&&(jQuery.isPlainObject(object)||(copyIsArray=jQuery.isArray(object)))){
if(copyIsArray){
copyIsArray=false;
clone=src&&jQuery.isArray(src)?src:[];
}else{
clone=src&&jQuery.isPlainObject(src)?src:{};
}
target[name]=jQuery.extend(deep,clone,copy);
}else{
target[name]=copy;
}
}
}
return target;
};
//检查是否是数组或者类数组
function isArrayLike(obj){
var length=obj.length,
type=jQuery.type(obj);
if(obj&&jQuery.isWindow(obj)){
return false;
}
if(obj.nodeType===&&length){
return true;
} if(type==='array'){
return true;
}
if(typeof length==='number'&&(length==||(length>&&(length-) in obj))){
return true;
}
return false;
}
jQuery.extend({
//一堆静态方法和属性
expando:'jQuery'+(core_version+Math.random()).replace(/\D/g,''),
// 该函数用于释放jQuery对于全局变量$的控制权,可选的参数deep代表是否释放对全局变量jQuery的控制权
noConflict:function(deep){
if(window.$===jQuery){
window.$=_$;
}
if(deep&&window.jQuery===jQuery){
window.jQuery=_jQuery;
}
return jQuery;
},
/********isReady,readyWait,holdReay,ready与加载事件有关,暂且略过***********/
isReady:false,
readyWait:,
holdReady:function(hold){},
ready:function(){},
/*******/ /****下面是一系列类型检测的静态方法*******/
isFunction:function(obj){
//如果使用typeof,在有些浏览器中,正则也会返回function,因此这里采用jQuery处理后的方法,jQuery.type
return jQuery.type(obj)==='function';
},
isArray:Array.isArray,
isWindow:function(obj){
return obj!==null&&obj===obj.window;
},
//判断obj是否为数字或者数字类型的字符串,并且是有效数字
isNumeric:function(obj){
return !isNaN(parseFloat(obj))&&isFinite(obj);
},
type:function(obj){
if(obj===null){
return String(null);
}
//Date,Array等类型typeof都会返回object,function、正则(部分浏览器)中 typeof都会返回function if(typeof obj==='object'||typeof obj==='function'){
return class2type[core_toString.call(obj)];
}
return typeof obj;
},
//判断是否为以下两种情况:1,对象字面量;2,通过new Object()创建
isPlainObject:function(obj){
if(jQuery.type(obj)!=='object'||obj.nodeType||jQuery.isWindow(obj)){
return false;
} //如果是纯粹的对象,那么obj一定有constructor属性,并且方法hasOwnPropertyOf一定就在构造函数本身的原型中,而不用通过原型链查找得到
if(obj.constructor&&!core_hasOwn.call(obj.constructor.prototype,'isPrototypeOf')){
return false;
}
return true; },
//检查是否是空对象
isEmptyObject:function(obj){
for(var name in obj){
return false;
}
return true;
},
/******类型检测静态方法结束********/ error:function(msg){
throw new Error(msg);
},
//将html字符串转换为html DOM结构,
parseHTML: function( data, context, keepScripts ){ },
parseJSON:JSON.parse,
parseXML:function(data){
var xml, tmp;
if ( !data || typeof data !== "string" ) {
return null;
} // Support: IE9
try {
tmp = new DOMParser();
xml = tmp.parseFromString( data , "text/xml" );
} catch ( e ) {
xml = undefined;
} if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
jQuery.error( "Invalid XML: " + data );
}
return xml;
},
noop:function(){},
//用于在全局作用域执行javascript代码,这里暂略
globalEval:function(data){},
//转换连字符字符串为驼峰类型
camelCase:function(string){
return string.replace(rmsPrefix,'ms-').replace(rdashAlpha,fcamelCase);
},
//判断elem的nodeName是否=name
nodeName:function(elem,name){
return elem.nodeName&&elem.nodeName.toLowerCase()==name.toLowerCase();
},
//jQuery遍历方法,其中args是传递给回调callback的参数,仅供jQuery内部使用;外部调用该方法时,回调的参数默认为数组下标/对象key,对应数组值/对象value
each:function(object,callback,args){
var i,
value,
length=object.length,
isArray=isArrayLike(object); if(args){//说明是内部调用
if(isArray){
for(i=;i<length;i++){
value= callback.call(object[i],args);
if(value===false){
break;
}
}
}else{
for(i in object){
value=callback.call(object[i],args);
if(value===false){
break;
}
}
}
}else{
if(isArray){
for(i=;i<length;i++){
value=callback.call(object[i],i,object[i]);
if(value===false){
break;
}
}
}else{
for(i in object){
value=callback.call(object[i],i,object[i]);
if(value===false){
break;
}
}
}
}
return object;
},
trim:function(str){
return str==null?'':core_trim.call(str);
},
//将一个类数组对象转换为真正的对象
//results参数仅供jquery内部使用,此时在该参数的基础上添加元素
makeArray:function(array,results){
var ret=results||[],
type=jQuery.type(array);
//undefined,null都会==null
if(array!=null){
//1,没有length属性,或者具有length属性,但是是以下几种情况的
//2.如果array是string 的length表示字符串的长度
//3.如果array是函数,其length代表函数生命时的参数个数
//4,如果array是window对象,属性Length返回窗口中的框架(frame,iframe)个数
if(array.length==null|| type=='string' || type=='function' ||type=='regexp'||jQuery.isWindow(array)){
core_push.call(ret,array);
}else{//否则说明是类数组对象
jQuery.merge(ret,array);
}
}
return ret;
}, inArray:function(elem,array,i){
return array==null?-:core_indexOf.call(array,elem,i);
},
//用于合并两个数组的元素到第一个数组中
//事实上,jquery源代码中第一个参数可以是数组或者类数组对象,第二个参数可以是数组、类数组对象或任何含有连续整型属性的对象
//第一个参数是数组,最后返回数组;第一个参数是类数组,则返回类数组
merge:function(first,second){
var l=second.length,
i=first.length,
j;
for(j=;j<l;j++){
first[i++]=second[j];
}
first.length=i;
return first;
},
//用于查找数组中满足过滤函数的元素,形成新的数组之后返回,原数组不受影响
//如果inv未传入或者是false,元素只有在过滤函数返回true时,才会被保存在最终的结果数组中
//如果参数inv是true,则恰好相反
grep:function(elems,callback,inv){
var i,
ret=[],
length=elems.length,
retVal;
inv=!!inv;
for(i=;i<length;i++){
retVal=!!callback.call(elems[i],i);
if(retVal!==inv){
ret.push(elems[i]);
}
}
return ret;
},
//用于对数组中每个元素执行callback操作,并将结果形成新的数组返回
//参数arg仅仅是jQuery内部使用
map:function(elems,callback,arg){
var ret=[],
retVal,
i,
length=elems.length,
isArray=isArrayLike(elems);
if(isArray){
for(i=;i<length;i++){
retVal=callback.call(elems[i],i,arg);
if(retVal!=null){
ret.push(retVal);
}
}
}else{
for(i in elems){
retVal=callback.call(elems[i],i,arg);
if(retVal!=null){
ret.push(retVal);
}
}
}
//保证最终返回的是一维数组
return core_concat.call([],ret);
},
guid:,
//该方法用于更改函数的执行上下文
//源代码中有两种传参形式,这里仅考虑最常见的一种
proxy:function(fn,context){
if(!jQuery.isFunction(fn)){
return undefined;
}
var args=core_slice.call(arguments,);
proxy=function(){
return fn.call(context||this,core_concat.call(args,core_slice.call(arguments)));
};
proxy.guid=fn.guid=fn.guid||jQuery.guid++;
return proxy;
},
//用一个方法同时实现get和set操作
//如何设置或者获取由回调函数fn确定
//这个方法的实现等用到的时候结合来看
access: function( elems, fn, key, value, chainable, emptyGet, raw ){ },
now:Date.now,
//该方法用于交换css样式,在support模块较多用到
//要交换的样式由参数options传递
swap: function( elem, options, callback, args ){
var name,ret,
old={};
for(name in options){
old[name]=elem.style[name];
elem.style[name]=options[name];
}
ret=callback.call(elem,args||[]);
for(name in options){
elem.style[name]=old[name];
}
return ret;
}, }); //目前,js中typeof的返回值有六种:"number," "string," "boolean," "object," "function," 和 "undefined."
//通过object.prototype.toString/或者{}.toString 返回值有九种:Boolean Number String Function Array Date RegExp Object Error,其中的Array,Date,RegExp,Object,Error都属于Object类型,在有些浏览器中typeof 正则会返回function
jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(i,name){
class2type["[object "+name+"]"]=name.toLowerCase();
});
//console.log(class2type,class2type);
rootjQuery=jQuery(document);
window.jQuery=window.$=jQuery;
})(window);

myJquery161010

上一篇:用Java实现菱形的打印输出


下一篇:Nagios安装配置教程(一)基本了解