基于jquery-2.0.3的源码学习
// 2.给JQ对象添加一些方法和属性
/*jQuery.fn = jQuery.prototype = { //添加实例属性和方法 prototype 属性使您有能力向对象添加属性和方法。
jquery : 版本
constructor : 修正指向问题
init() : 初始化和参数管理
selector : 存储选择字符串
length : this对象的长度
toArray() : 转数组
get() : 转原生集合
pushStack() : JQ对象的入栈
each() : 遍历集合
ready() : DOM加载的接口
slice() : 集合的截取
first() : 集合的第一项
last() : 集合的最后一项
eq() : 集合的指定项
map() : 返回新集合
end() : 返回集合前一个状态
push() : (内部使用)
sort() : (内部使用)
splice() : (内部使用)
};*/
jQuery.fn = jQuery.prototype ={ //prototype 属性使您有能力向对象添加属性和方法。
jquery:core_version,//jq的版本号
constructor:jQuery,//修正指向问题 因为是简写的所以要写此代码指定一下指向
init:function(selector,context,rootjQuery){ //初始化和参数管理 selector代表选择元素
var match, elem;
// HANDLE: $(""), $(null), $(undefined), $(false)
if(!selector){ //如果选择的不是正确的元素直接返回
return this;
}
// Handle HTML strings选择的是字符串如$('#div1') $('.box') $('div') $('#div1 div.box') $('<li>') $('<li>1</li><li>2</li>')
if(typeof selector ==="string"){//处理选择的元素是字符串类型 charAt() 方法可返回指定位置的字符。
if(selector.charAt(0)==="<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >=3){ //找标签
//如果是单标签$('<li>')则match=[null,'<li>',null]
//如果是多标签如$('<li>1</li><li>2</li>')则match=[null,'<li>1</li><li>2</li>']
match = [null,selector,null];
}else{
// 把selector进行匹配,如果匹配成功,
//match数组第一个元素是selector,
//第二个元素是HTML或者是undefined,
//第三个元素是ID或者是undefined。
match = rquickExpr.exec( selector );
}
// Match html or make sure no context is specified for #id
// 判断字符串是否是一个单独的标签
// 如果match[1]不是undefined,即参数selector是HTML代码,或者match[2]不是undefined,即参数selector是#id,
//并且未传入参数context。完整版的判断如下“if (match && (match[1] || match[2] && !context)) {}”,
//为什么省略了对match[2]的判断?因为如果match不是null且match[1]是undefined,
//那么此时match[2]必然不是undefined,所以对match[2]的判断可以省略。
if(match && (match[1] || !context) ){ //如果是标签或id进入if
// HANDLE: $(html) -> $(array)
//判断字符串是html
if(match[1]){
//instanceof运算符用来判断一个构造函数的prototype属性所指向的对象是否存在另外一个要检测对象的原型链上
//修正context:“context = context instanceof jQuery ? context[0] : context;”。
//先判断第二个参数的类型在将context赋值成原生的节点。例如输入的是:$('li',document)或$('li',$(document))。
context = context instanceof jQuery ? context[0]:context;
//jQuery.parseHTML()用于将HTML字符串解析为对应的DOM节点数组。有三个参数:htmlString,context,keepScripts。
//HTMLString,string类型,需要解析并转为DOM节点数组的字符串。
//context,element类型。指定在那个document中创建元素。默认为当前文档的document。
//keepscript,boolean类型,指定传入的字符串中是否包含脚本,默认为false。
//$.merge() 函数用于合并两个数组内容到第一个数组。
// 我们传入了三个参数:"match[ 1 ],
//context && context.nodeType ? context.ownerDocument || context : document,true"。
//如果context与context的节点存在,使用context或context的owner document,否则用默认参数domcument。
// jQuery.merge()用于合并两个数组内容到第一个数组。注意这时传入的this是个json对象而不是数组,通过这种方式也能进行合并并返回jQuery想要的json格式。
jQuery.merge(this,jQuery.parseHTML(
match[1],
context && context.nodeType ? context.ownerDocument || context : document,
true
));
// 解析$(HTML, props)格式
// 如果正则rsingleTag验证"match[1]"是否是一个单独的标签,且context是一个纯粹的对象条件成立。
//循环这个json对象,并判断json里的属性是否是jq自带的方法,如果是,则直接调用方法,否则,用jq的attr方法为这个标签加一个“match”属性。
if(rsingleTag.test(match[1])&& jQuery.isPlainObject( context )){
for(match in context){
if(jQuery.isFunction( this[ match ] )){ //$.isFunction()函数用于判断指定参数是否是一个函数。
this[ match ]( context[ match ] );
}else{
this.attr( match, context[ match ] );//否则,用jq的attr方法为这个标签加一个“match”属性。
}
}
}
//返回当前对象
return this;
}
//判断字符串是ID且未指定参数
else{
//match数组第一个元素是selector,
//第二个元素是HTML或者是undefined,
//第三个元素是ID或者是undefined。
//使用getElementById()方法查找含有Id属性的DOM元素
elem = document.getElementById(match[2]);
//如果DOM元素存在,设置第一个元素,属性length,并返回当前对象。
if(elem && elem.parentNode){
this.length =1;
this[0] = elem;
}
this.context = document;
this.selector = selector;
return this;
}
}// 判断字符串是选择器表达式
else if(!context || context.jquery ){
// 如果没有指定上下文,则执行root.find(selector);如果指定了上下文,且上下文是jQuery对象,则执行context.find(selector);
return ( context || rootjQuery ).find( selector );
}else{
// 如果制定了上下文,但上下文不是jQuery对象,则执行
//find() 方法获得当前元素集合中每个元素的后代,通过选择器、jQuery 对象或元素来筛选。
return this.constructor( context ).find( selector );
}
}else if(selector.nodeType){//选择节点的方式
// 如果参数selector含有属性nodeType,则认为selector是DOM元素,
//设置第一个元素指向该DOM元素、属性length为1,
//然后返回包含了改DOM元素引用的jQuery对象。:
//nodeType声明了文档树中节点的类型,
//例如,element节点的该属性值是1,text节点是3,comment是9,document是9,documentfragment节点是11。
// 参数selector是节点,设置第一个元素、属性length,并返回当前对象。
this.context = this[0] = selector;
this.length = 1;
return this;
}else if(jQuery.isFunction(selector)){ // 参数selector是函数
return rootjQuery.ready(selector);
}
if(selector.selector !== undefined){ //selector是jquery对象
this.selector = selector.selector;
this.context = selector.context;
}
return jQuery.makeArray( selector, this ); //$.makeArray() 函数用于将一个类似数组的对象转换为真正的数组对象。
},
selector:"",//存储选择字符串
length:0,//this对象的长度length表示当前jQuery对象中元素的个数。
toArray:function(){ //转数组方法将当前jQuery对象转换为真正的数组,转换后的数组包含了所有的元素
// 连同slice()一起声明的还有concat()、push()、indexOf()、toString()、hasOwn()、fnToSting()、objectFunctionSting()这些方法。
//这里通过声明这些核心方法的引用,
//使的在jQuery代码中可以借用这些核心方法的功能,执行时可通过方法call()和apply()指定方法执行的环境,即关键字this所引用的对象。
return core_slice.call( this );//slice() 方法用数组的某个片段切出新数组
},
get:function(num){ //get()转原生集合 get() 方法获得由选择器指定的 DOM 元素。
//当get方法没有传参数时用toArray()方法将对象转换为真正的数组
//如果没有传入参数,则返回包含了所有元素的新数组
//当get方法传参数时则返回一个单独的元素;参数index从0开始计算,并且支持负数,负数表示从元素集合末尾开始计算。
return num == null ? this.toArray() : (num < 0 ? this[ this.length + num] :this[num] )
},
pushStack:function( elems ){ //jq对象的入栈处理
//构造一个新的空jQuery对象ret,this.constructor指向构造函数函数jQuery,并把参数elems合并到jQuery对象ret中。
var ret = jQuery.merge(this.constructor(),elems); //$.merge() 函数用于合并两个数组内容到第一个数组
// 在新的jQuery对象ret上设置属性prevObject,指向当前jQuery对象,从而形成一个链式栈。
//因此,方法.pushStack()的行为还可以理解为,
//构建一个新的jQuery对象并入栈,新对象位于栈顶,这也是该方法如此命名的原因所在。
ret.prevObject = this;//在新Query对象ret上设置属性prevObject,指向当前jQuery对象,从而形成一个链式栈
ret.context = this.context;
return ret;// 最后返回新jQuery对象ret。
},
//方法.each()遍历当前jQuery对象,并在每个元素上执行回调函数。
//每当回调函数执行时,会传递当前循环次数作为参考,
//循环次数从0开始计数;更重要的是,
//回调函数是在当前元素为上下文的语境中触发的,
//即关键字this总是指向当前元素;在回调函数中返回false可以终止遍历。
each:function(callback, args){//each() : 遍历集合
return jQuery.each(this,callback,args); //each() 方法规定为每个匹配元素规定运行的函数。
},
ready:function(fn){ //ready() : DOM加载的接口
jQuery.ready.promise().done(fn);
return this;
},
slice:function(){ //slice() : 集合的截取
//$("div").slice(1,3).css().end().css(),假设有4个div,那么第一个css只对第二个和第三个有效果,end之后,第二个css对这4个div都有效果。
// .slice(start,[end])将匹配元素集合缩减为指定范围的子集
//apply()应用某一对象的一个方法,用另一个对象替换当前对象。
return this.pushStack(core_slice.apply(this,arguments));//4个div.pushStack(2个div)
},
first:function(){ //first()集合的第一项
return this.eq(0);
},
last:function(){//last()集合的最后一项
return this.eq(-1);
},
eq:function(i){ //eq() : 集合的指定项
var len = this.length;
j = +i + (i < 0 ? len :0);
return this.pushStack(j >= 0 && j < len ? [this[j]] : []);
},
//map()遍历当前jQuery对象,在每个元素上执行回调函数,
//并将回调函数的返回值放入一个新jQuery对象中。
//该方法常用于获取或设置DOM元素集合的值。
map:function(callback){ //map() : 返回新集合
//$.map() 函数用于使用指定函数处理数组中的每个元素(或对象的每个属性),并将处理结果封装为新的数组返回
// 执行回调函数时,关键字this指向当前元素。
//回调函数可以返回一个独立的数据项或数据项数组,
//返回值将被插入结果集中;如果返回一个数组,
//数组中的元素会被插入结果集;如果回调函数返回null或undefined,则不会插入任何元素。
return this.pushStack(jQuery.map(this,function(elem,i){
return callback.call(elem,i,elem);
}));
},
//end() 方法结束当前链条中的最近的筛选操作,并将匹配元素集还原为之前的状态。
end:function(){ //返回集合前一个状态
// 返回前一个jQuery对象。如果属性prevObject不存在,则构建一个空的jQuery对象返回。
// 方法.pushStack()用于入栈。方法.end()则用于出栈。
return this.prevObject || this.constructor();
},
// 方法.push(value,...)向当前jQuery对象的末尾添加新元素,并返回新长度。
push:core_push,//push() 方法(在数组结尾处)向数组添加一个新的元素
// 方法.sort([orderfunc])对当前jQuery对象中的元素进行排序,可以传入一个比较函数来指定排序方式。
sort:[].sort,//数组的排序内部使用
//方法.splice(start,deleteCount,value,...)向对前jQuery对象中插入、删除或替换元素。
splice: [].splice //splice() 方法可删除从 index 处开始的零个或多个元素,并且用参数列表中声明的一个或多个值来替换那些被删除的元素。
}
// 2.给JQ对象添加一些方法和属性