前言:前面一篇已经看过$是如何封装jQuery对象的,可以简单的概述为,把DOM对象放在了属性名为0、1、2....下面,然后给jQuery添加了context,length,selector属性,还有一些实例出来的方法。
【jquery源码二】$选择器--是如何将DOM封装成jquery对象的①
这篇文章来说说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 );