jQuery(版本2.0.3)在96-283行中给JQ对象,添加一些方法和属性
下载地址:https://code.jquery.com/jquery/版权声明:以下为本人在妙味课堂听课的笔记
96-283行大体结构如下:
jQuery.fn = jQuery.prototype = { //添加实例属性和方法
jquery : 版本
constructor : 修正指向问题
init() : 初始化和参数管理
init大体框架:
init:function(){
if(1、$(""), $(null), $(undefined), $(false)){
return this;//
}
if(2、$('#XXX') $('.XXX') $('XXX') $('#XXX XXX')根据类、范围、标签类型或者id选择;3、$('<XXX>') $('<XXX>123') $('<XX>X</XX><XX>2</XX>')创建标签){
if($('<XXX>')或者$('<XX>X</XX><XX>2</XX>')){
match = [ null, '<XXX>', null ];或者match = [ null, '<XX>X</XX><XX>2</XX>', null ];
}else(){
如果参数为$('#XXX'),match=['#XXX',null,'XXX'];
如果参数为$('<XXX>123'),match=['<XXX>123','<XXX>,null]';
否则,match=null;
}
if($('<XXX>') || $('<XX>X</XX><XX>2</XX>') || $('<XXX>123') || $('#XXX')){
if($('<XXX>') || $('<XX>X</XX><XX>2</XX>') || $('<XXX>123')){
parseHTML将参数字符串转成数组,每个标签为数组的一项,
if($('<XXX>',{attr:v})){
创建出标签对象;
if(如果属性有对应方法的话,调用方法){
添加属性;
}else{
如果属性没有对应的方法则通过attr进行操作;
}
return this;返回创建出来的标签;
}
}else{
如果是根据id选择的话,返回id筛选出的元素;
}
}else if(是$(expr, $(...))这种复杂选择器的话){
丢给find()函数处理;
}
else if(是$(expr, 原生的js对象)这种复杂选择器的话){
丢给find()函数处理;
}
}else if(如果是节点的话,比如document,window等){
将上下文和json对象同时指向该节点对象,并返回对象;
}else if(5、$(function(){})){
返回document.ready(selector),比如$(fucntion(){DOM元素加载完再执行函数体});
}
if(有人强行要写成$($('#div'))的话){
重新说明下指向就好了
}
return $([])、$({})处理后返回的jason
}
selector : 存储选择字符串
length : this对象的长度
toArray() : 转数组
get() : 转原生集合
pushStack() : JQ对象的入栈
each() : 遍历集合
ready() : DOM加载的接口
slice() : 集合的截取
first() : 集合的第一项
last() : 集合的最后一项
eq() : 集合的指定项
map() : 返回新集合
end() : 返回集合前一个状态
push() : (内部使用)
sort() : (内部使用)
splice() : (内部使用)
};
详细讲解
jQuery.fn = jQuery.prototype = {
// The current version of jQuery being used
jquery: core_version,
constructor: jQuery,
/*constructor用于修正指向问题,原生js中我们创建出来一个类之后,
会自动在该类的原型中加入constructor属性指向自身的构造函数(详细讲解见:http://www.jb51.net/article/22334.htm)
但是constructor非常容易被修改,当类的prototype被覆盖时,如
Aaa.prototype = {
constructor : Aaa,
name : 'hello',
age : 30
};
此时类Aaa将指向Object,所以在这边重新指向下
*/
init: function( selector, context, rootjQuery ) {//三个参数分别为:选择器,上下文,document
var match, elem;
/*我们jQuery时,参数可以分为以下几类:可参考
http://www.w3school.com.cn/jquery/core_jquery.asp
http://www.w3school.com.cn/jquery/jquery_ref_selectors.asp
1、$(""), $(null), $(undefined), $(false)
2、$('#XXX') $('.XXX') $('XXX') $('#XXX XXX')根据类、范围、标签类型或者id选择
3、$('<XXX>') $('<XXX>123') $('<XX>X</XX><XX>2</XX>')创建标签
4、$(this) $(document) $(window)
5、$(function(){}):全写为 jQuery(document).ready(function(){ }); 表示dom元素加载完再执行函数体
6、$([]) $({})
*/
// HANDLE: $(""), $(null), $(undefined), $(false)
//这边用于处理第一种情况,当参数不符合要求时直接返回
if ( !selector ) {
return this;
}
// Handle HTML strings
if ( typeof selector === "string" ) {
if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
// Assume that strings that start and end with <> are HTML and skip the regex check
match = [ null, selector, null ];
//如果参数符合第三种情况中的$('<XXX>')或者$('<XX>X</XX><XX>2</XX>'),也就是创建标签的时候
} else {
match = rquickExpr.exec( selector );
/*
rquickExpr可以匹配$('#XXX'),'<XXX>123'。exec方法用于匹配正则,如果能匹配到,返回值为[匹配的字符串本身,第一个组,第二个组。。。],如果不能匹配,则返回null
*/
}
// Match html or make sure no context is specified for #id:创建标签,或者通过没有上下文来判断id,即$('<XXX>') || $('<XX>X</XX><XX>2</XX>') || $('#XXX') || $('<XXX>123')
if ( match && (match[1] || !context) ) {
// HANDLE: $(html) -> $(array)
if ( match[1] ) {
context = context instanceof jQuery ? context[0] : context;//得到原生的document
// scripts is true for back-compat
jQuery.merge( this, jQuery.parseHTML(
//parseHTML将字符串转成节点数组,jQuery.parseHTML('<li>1</li><li>2</li>',document,true); 返回['li','li'],其中第二个参数表示上下文,第三个参数表示是否允许字符串中的script标签添加进来
/*merge在jq内部允许json合并,$('<li>1</li><li>2</li>')在jq源码中会生成,
this = {
0 : 'li',
1 : 'li',
length : 2
}这样一个json对象(可以自己调用输出下$(‘div’)或其他的进行试验),merge用于将得到的数组转化成json对象,便于后续处理。
如$('<li>1</li><li>2</li>').css('background','#000'),会进行for循环处理每个li
for(var i=0;i<this.length;i++){
this[i].style.background = 'red';
}
*/
match[1],
context && context.nodeType ? context.ownerDocument || context : document,
true
) );
// HANDLE: $(html, props)创建的标签带有属性时,如:$('<li></li>',{title : 'hi',html : 'abcd',css : {background:'red'}}).appendTo( 'ul' );
if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
for ( match in context ) {//如果属性有对应的方法
// Properties of context are called as methods if possible
if ( jQuery.isFunction( this[ match ] ) ) {
this[ match ]( context[ match ] );
// ...and otherwise set as attributes
} else {//如果属性没有对应的方法则通过attr进行操作
this.attr( match, context[ match ] );
}
}
}
//返回创建出来的标签对象
return this;
// HANDLE: $(#id)
} else {
elem = document.getElementById( match[2] );//如果参数为$('#XXX'),match=['#XXX',null,'XXX'];缩略版中已讲。所以此处match[2]正好是元素的id
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
if ( elem && elem.parentNode ) {//elem.parentNode主要用于处理黑莓浏览器的一个问题
// Inject the element directly into the jQuery object
this.length = 1;
this[0] = elem;
/*
上文中已说过,jq源码中获取或生成的元素以
this = {
0 : 'li',
1 : 'li',
length : 2
}这样一个json对象存储,初始化时length为0,在获取到对象之后重新赋值*/
}
this.context = document;
this.selector = selector;
return this;
}
// HANDLE: $(expr, $(...))
} else if ( !context || context.jquery ) {//$('ul',$(document)).find('li');当传入的context为jq对象时
return ( context || rootjQuery ).find( selector );
// HANDLE: $(expr, context)
// (which is just equivalent to: $(context).find(expr)
} else {//$('ul',document).find('li');当传入的context为原生元素时
return this.constructor( context ).find( selector );
}
// HANDLE: $(DOMElement)
} else if ( selector.nodeType ) {
this.context = this[0] = selector;
this.length = 1;
return this;
// HANDLE: $(function)
// Shortcut for document ready
} else if ( jQuery.isFunction( selector ) ) {
return rootjQuery.ready( selector );
}
if ( selector.selector !== undefined ) {//$($('#div'))
this.selector = selector.selector;
this.context = selector.context;
}
return jQuery.makeArray( selector, this );
/*将$([]) $({})中传入的对象做处理,返回
this = {
0 : 'li',
1 : 'li',
length : 2
}这样一个json对象存储,与上文中merge函数相似*/
},
// Start with an empty selector
selector: "",
// The default length of a jQuery object is 0
length: 0,
toArray: function() {
return core_slice.call( this );
/*上文中core_slice = core_deletedIds.slice将数组的slice方法存储在core_slice中,
所以toArray的作用就是将对象转化成一个原生的数组。
比如页面上有三个div,那么$('div')得到的是 { 0 : div , 1 : div , 2 : div , length : 3 }
$('div') : { 0 : div , 1 : div , 2 : div , length : 3 }
$('div').toArray() 得到的是 [div,div,div]
*/
},
// Get the Nth element in the matched element set OR
// Get the whole matched element set as a clean array
get: function( num ) {
return num == null ?
// Return a 'clean' array
this.toArray() :
// Return just the object
( num < 0 ? this[ this.length + num ] : this[ num ] );
/*
$('div').get()返回原生数组(调用了toArray方法,),
$('div').get(num),返回第num个元素(原生的)。
值得注意的是,这边的this[ num ]是指this.0,this.1,this.2...
原生js中规定,取得元素属性的时候有两种方法,一种是. ,当属性名是变量是可以使用[]
*/
},
// Take an array of elements and push it onto the stack
// (returning the new matched element set)
pushStack: function( elems ) {
/*
入栈处理,学过数据结构的朋友都应该了解栈这个概念。栈的特点就是后入先出。
此函数在内部调用较多。举个例子:
$('div').find('span').css('background','red').end().css('background','yellow');
执行$('div').find('span')时$('div')先入栈,span再入栈(所以是span先出栈div再出栈),
所以css方法执行时是针对的span。如果我们想要对栈的下一层(也就是先入栈的div)操作,
可以使用end方法回溯到栈的下一层,使当前对象指向div。
栈的操作不仅为jq中很多链式操作提供了便利,也为jq整体提供了清晰的结构和逻辑
*/
// Build a new jQuery matched element set
var ret = jQuery.merge( this.constructor(), elems );//给新入栈的元素添加一个prev属性,为end方法做准备,便于回溯
// Add the old object onto the stack (as a reference)
ret.prevObject = this;
ret.context = this.context;
// Return the newly-formed element set
return ret;
},
// Execute a callback for every element in the matched set.
// (You can seed the arguments with an array of args, but this is
// only used internally.)
each: function( callback, args ) {
return jQuery.each( this, callback, args );
},
ready: function( fn ) {
// Add the callback
jQuery.ready.promise().done( fn );
return this;
},
slice: function() {//将切割后的数组push到栈中
return this.pushStack( core_slice.apply( this, arguments ) );//apply方法详解见http://blog.csdn.net/business122/article/details/8000676
},
first: function() {
return this.eq( 0 );
},
last: function() {
return this.eq( -1 );
},
eq: function( i ) {//将eq得到的元素push进栈并返回
var len = this.length,
j = +i + ( i < 0 ? len : 0 );
return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
/*
前边说过,jq中获取到的集合存储形式为:{
0:XXX,
1:YYY,
length:2
},所以这边直接返回this[j]就好了
*/
},
map: function( callback ) {//遍历数组或jason等集合中的key和value
return this.pushStack( jQuery.map(this, function( elem, i ) {
return callback.call( elem, i, elem );
}));
},
end: function() {//回溯到栈的下一层
return this.prevObject || this.constructor(null);
},
// For internal use only.
// Behaves like an Array's method, not like a jQuery method.
push: core_push,
sort: [].sort,
splice: [].splice
};