1. 程式人生 > >【jquery原始碼二】$選擇器--是如何將DOM封裝成jquery物件的②

【jquery原始碼二】$選擇器--是如何將DOM封裝成jquery物件的②

前言:前面一篇已經看過$是如何封裝jQuery物件的,可以簡單的概述為,把DOM物件放在了屬性名為0、1、2....下面,然後給jQuery添加了context,length,selector屬性,還有一些例項出來的方法。

這篇文章來說說jQuery是如何實現眾多選擇器效果的。

一、基本架構。

1、先來看看幾種選擇器的使用

①、建立html: $('<div>')、$('<div>1</div><div></div>')、$('<div>hello')

    ②、常規選擇: $('#id')、$('.class')、$('div')

    ③、複雜選擇: $('.class',$(document))、$('.class',document)、$('#id .class')、$('div.class') 、$('input[type="text"]')

    ④、將DOM轉換成jQueryd物件:$(document)、$(this)、$(DOM)

    ⑤、特殊選擇:$(function(){}),$([]),$({})

2、再看看這幾種選擇在init()都是走哪

(function(window,undefined){  
    var rootjQuery = jQ(document),
	rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/;
		
    var jQuery = function(selector){  
	return new jQuery.fn.init(selector);  
    };  
    jQuery.fn = jQuery.prototype = {  
	jquery:'2.0.3',       //jquery版本號資訊
	constructor: jQuery,  //新增構造器屬性
	length:0,	      //初始length屬性
	selector:'',	      //初始selector屬性
	init: function(selector, context, rootjQuery){   
	    var match, elem;

	    // 如果是: $(""), $(null), $(undefined), $(false)  直接return jQuery物件
	    if ( !selector ) {  return this; }
			
	    if( typeof selector === "string"){  //判斷selector是否是string型別

		if(selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3){
		    //判斷是否是 $('<div>')、$('<div>1</div><div></div>')
		}else{
		    /*
		        selector是string型別的,
			除了$('<div>')、$('<div>1</div><div></div>'),都會走一次這裡
		    */
		}
		
		if( match[1] && (match[1] || !context) ){   
				
		    if( match[1] ){    
			//$('<div>')、$('<div>1</div><div></div>')
		    }else{
			//$('#id')
		    }
			
		}else if(!context || context.jquery){
		    //$('.class')、$('div')、$('<div>hello')
		    //$('.class',$(document))、$('#id .class')、$('div.class') 、$('input[type="text"]')
		}else{
		    //$('div',document)
		}
		
	    }else if( selector.nodeType ){    //判斷selector是DOM節點物件
		//$(document)、$(this)、$(DOM)
	    }else if( jQuery.isFunction(selector )){   //判斷selector是函式
		//$(function(){})
	    }
		
	    if(selector.selector !== undefined ){
		//特殊處理已經是jquery物件的$($('#id'))
	    }  
		  
	    return jQuery.makeArray( selector, this );   //$([]),$({})
	    }
    }
	
    jQuery.fn.init.prototype = jQuery.fn;  
	
    window.$ = window.jQuery = jQuery;    	
})( window ); 

二、程式碼解析。

1、選擇器引數是string型別的

if( typeof selector === "string"){  //判斷selector是否是string型別

    if(selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3){
	// $('<div>') -> match = [null, '<div>', null];
	// $('<div>1</div><div>2</div>') -> match = [null, '<div>1</div><div>2</div>', null]
	match = [ null, selector, null ];
    }else{
	match = rquickExpr.exec( selector );
	//$('#div') -> match=['#div', undefined, 'div'] 
	//$('<div>hello') -> match=['<div>hello','<div>', undefined] 
	//$('.class')、$('div')、$('#id .class')、$('div.class') 、$('input[type="text"]') -> match = null
    }
		
    if( match[1] && (match[1] || !context) ){   
				
	if( match[1] ){    
		context = context instanceof jQuery ? context[0] : context;
	        jQuery.merge( this, jQuery.parseHTML(  
		    match[1],
		    context && context.nodeType ? context.ownerDocument || context : document,
		    true
		));
		//$('<div>',{title:'hi',html:'abcd',css:{background:red}}); 
		if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {  
                    // 新增屬性只支援單標籤,而且context是物件自變數(json格式)
						
		    for ( match in context ) {    //對第二個引數物件自變數進行for in 迴圈。
		        //如果是this[match]是jQuery方法的話就呼叫方法,如果不是的話就用attr進行屬性新增 
		        if ( jQuery.isFunction( this[ match ] ) ) {   //$().html、$().css  
			    this[ match ]( context[ match ] );   //$().html('abcd')
		        } else {
			    this.attr( match, context[ match ] );   //通過attr進行新增
		        }
		    }
		}
		return this;
        }else{	//$('#id')   match=['#id',null,'id'] 
	    elem = document.getElementById( match[2] );   
	    if ( elem && elem.parentNode ) {
		this.length = 1;
		this[0] = elem;
	    }
	    this.context = document;
	    this.selector = selector;
	    return this;
	}
			
    }else if(!context || context.jquery){          
        //$('.class')、$('div')、$('<div>hello')
        //$('.class',$(document))、$('#id .class')、$('div.class') 、$('input[type="text"]')
      //!context有沒實參(沒有實參的話進入這裡) || 判斷context是不是jquery物件(通過檢視有沒有jquery版本號屬性) 

      return ( context || rootjQuery ).find( selector );   //$(document).find(selector)
  }else{
     //$('div',document)return this.constructor( context ).find( selector );  
     //jQuery(document).find('div');    
  }
}

2、選擇器引數是DOM節點的

else if( selector.nodeType ){	//是DOM節點物件的話都有nodeType屬性
    //$(document)、$(this)、$(DOM)
    this.context = this[0] = selector;
    this.length = 1;
    return this;
}

3、選擇器引數是function()

else if( jQuery.isFunction(selector )){
    //$(function(){})
    return rootjQuery.ready( selector );
}

4、選擇器是jQuery物件

if(selector.selector !== undefined ){
    //特殊處理已經是jquery物件的$($('#id'))
    this.selector = selector.selector;
    this.context = selector.context;
}  

5、特殊引數$([]),$({})

return jQuery.makeArray( selector, this );   //$([]),$({})

6、合併

(function(window,undefined){  
	var rootjQuery = jQ(document),
		rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/;
		
	var jQuery = function(selector){  
		return new jQuery.fn.init(selector);  
	};  
	jQuery.fn = jQuery.prototype = {  
		jquery:'2.0.3',       //jquery版本號資訊
		constructor: jQuery,  //新增構造器屬性
		length:0,			  //初始length屬性
		selector:'',		  //初始selector屬性
		init: function(selector, context, rootjQuery){   
			var match, elem;

			// 如果是: $(""), $(null), $(undefined), $(false)  直接return jQuery物件
			if ( !selector ) {  return this; }
			
			if( typeof selector === "string"){  //判斷selector是否是string型別

				if(selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3){
					// $('<div>') -> match = [null, '<div>', null];
					// $('<div>1</div><div>2</div>') -> match = [null, '<div>1</div><div>2</div>', null]
					match = [ null, selector, null ];
				}else{
					match = rquickExpr.exec( selector );
					//$('#div') -> match=['#div', undefined, 'div'] 
					//$('<div>hello') -> match=['<div>hello','<div>', undefined] 
					//$('.class')、$('div')、$('#id .class')、$('div.class') 、$('input[type="text"]') -> match = null
				}
		
				if( match[1] && (match[1] || !context) ){   
				
					if( match[1] ){    
						context = context instanceof jQuery ? context[0] : context;
						jQuery.merge( this, jQuery.parseHTML(  
							match[1],
							context && context.nodeType ? context.ownerDocument || context : document,
							true
						) );
						//$('<div>',{title:'hi',html:'abcd',css:{background:red}}); 
						if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {  // 新增屬性只支援單標籤,而且context是物件自變數(json格式)
						
							for ( match in context ) {    //對第二個引數物件自變數進行for in 迴圈。
								//如果是this[match]是jQuery方法的話就呼叫方法,如果不是的話就用attr進行屬性新增 
								if ( jQuery.isFunction( this[ match ] ) ) {   //$().html、$().css  
									this[ match ]( context[ match ] );   //$().html('abcd')
								} else {
									this.attr( match, context[ match ] );   //通過attr進行新增
								}
							}
						}
						return this;
					}else{	//$('#id')   match=['#id',null,'id'] 
						elem = document.getElementById( match[2] );   
						if ( elem && elem.parentNode ) {
							this.length = 1;
							this[0] = elem;
						}
						this.context = document;
						this.selector = selector;
						return this;
					}
			
				}else if(!context || context.jquery){  //$('div',$(document))
					//!context有沒實參(沒有實參的話進入這裡) || 判斷context是不是jquery物件(通過檢視有沒有jquery版本號屬性) 
					return ( context || rootjQuery ).find( selector );   //$(document).find(selector)
				}else{
					//$('div',document)
					return this.constructor( context ).find( selector );   //jQuery(document).find('div');
				}
		
			}else if( selector.nodeType ){	//是DOM節點物件的話都有nodeType屬性
				//$(document)、$(this)、$(DOM)
				this.context = this[0] = selector;
				this.length = 1;
				return this;
			}else if( jQuery.isFunction(selector )){
				//$(function(){})
				return rootjQuery.ready( selector );
			}
		
			if(selector.selector !== undefined ){
				//特殊處理已經是jquery物件的$($('#id'))
				this.selector = selector.selector;
				this.context = selector.context;
			}  
		  
			return jQuery.makeArray( selector, this );   //$([]),$({})
		}
	}
	
	jQuery.fn.init.prototype = jQuery.fn;  
	
	window.$$ = window.jQuery = jQuery;    	
})( window );