jQuery源码阅读笔记(96-283行)

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
};



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值