【jquery原始碼二】$選擇器--是如何將DOM封裝成jquery物件的②
阿新 • • 發佈:2019-01-24
前言:前面一篇已經看過$是如何封裝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 );