jQuery.fn.init()负责解析参数selector和context的类型,并执行相应的逻辑,最后返回jQuery.fn.init()的实例。
支持12种类型,如下所示:
- selectort可以转换成false,示例:$(""), $(null), $(undefined), $(false)。
- selectort为DOM元素,示例:$(DOMElement)。
- selectort为字符串,具体为"body",示例:$('body')。
- selectort为字符串,具体为”单独标签“,示例:$('<div>')。
- selectort为字符串,具体为”复杂HTML代码“,示例:$('<div>abc</div>')。
- selectort为字符串,具体为”#id“,context为undefined,示例:$('#id')。
- selectort为字符串,具体为”选择器表达式“,context为undefined,示例:$('div p')。
- selectort为字符串,具体为”选择器表达式“,context为jQuery对象,示例:$('div p',$('#id'))。
- selectort为字符串,具体为”选择器表达式“,context为DOM元素,示例:$('div p',this)。
- selectort为函数,示例:$(function(){})。
- selectort为jQuery,示例:$($('div'))。
- selectort为其他任意类型的值,示例:$([123])。
// 代码行:2903——3014
var rootjQuery,
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,
// rquickExpr包含两个分组,一个匹配HTML、一个匹配ID。
init = jQuery.fn.init = function( selector, context, root ) {
//参数selector可以是任意类型的值,但只有undefined、DOM元素、字符串、函数、jQuery对象、普通JavaScript对象这几种类型是有效的,其他类型的值也可以接受但没有意义。
//参数context可以不传,或传入DOM元素、jQuery对象、普通JavaScript对象之一。
//参数root用于document.getElementById()查找失败、selector是选择器表达式且未指定context、selector是函数三种情况情况。
var match, elem;
// HANDLE: $(""), $(null), $(undefined), $(false)
// 参数selector可以转化为false
//如果传入的是:空字符串、null、undefined、false,则直接返回this。这时this是空jQuery对象,其属性length为0。
if ( !selector ) {
return this;
}
// Method init() accepts an alternate rootjQuery
// so migrate can support jQuery.sub (gh-2101)
// 重置root:rootjQuery包含了document对象的jQuery对象,以此支持jquery.sub
root = root || rootjQuery;
// 参数selector是字符串
if ( typeof selector === "string" ) {
// 如果参数selector以“<”开头以“>”结尾,且长度大于3。则“假设”这个字符串是HTML片段,跳过正则表达式检查。否则就用正则rquickExpr检查字符串是否是ID、标签、类选择器,匹配的结果放到match数组中。
if ( selector[ 0 ] === "<" &&
selector[ 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 ];
} else {
// 把selector进行匹配,如果匹配成功,match数组第一个元素是selector,第二个元素是HTML或者是undefined,第三个元素是ID或者是undefined。
match = rquickExpr.exec( selector );
}
// Match html or make sure no context is specified for #id
// 判断字符串是否是一个单独的标签
// 如果match[1]不是undefined,即参数selector是HTML代码,或者match[2]不是undefined,即参数selector是#id,并且未传入参数context。完整版的判断如下“if (match && (match[1] || match[2] && !context)) {}”,为什么省略了对match[2]的判断?因为如果match不是null且match[1]是undefined,那么此时match[2]必然不是undefined,所以对match[2]的判断可以省略。
if ( match && ( match[ 1 ] || !context ) ) {
// HANDLE: $(html) -> $(array)
// 判断字符串是HTML
if ( match[ 1 ] ) {
//修正context:“context = context instanceof jQuery ? context[0] : context;”。先判断第二个参数的类型在将context赋值成原生的节点。例如输入的是:$('li',document)或$('li',$(document))。
context = context instanceof jQuery ? context[ 0 ] : context;
//判断能否向后兼容:“jQuery.merge(this, jQuery.parseHTML())”。
// jQuery.parseHTML()用于将HTML字符串解析为对应的DOM节点数组。有三个参数:htmlString,context,keepScripts。HTMLString,string类型,需要解析并转为DOM节点数组的字符串。context,element类型。指定在那个document中创建元素。默认为当前文档的document。keepscript,boolean类型,指定传入的字符串中是否包含脚本,默认为false。
// 我们传入了三个参数:"match[ 1 ],context && context.nodeType ? context.ownerDocument || context : document,true"。如果context与context的节点存在,使用context或context的owner document,否则用默认参数domcument。
// jQuery.merge()用于合并两个数组内容到第一个数组。注意这时传入的this是个json对象而不是数组,通过这种方式也能进行合并并返回jQuery想要的json格式。
jQuery.merge( this, jQuery.parseHTML(
match[ 1 ],
context && context.nodeType ? context.ownerDocument || context : document,
true
) );
// HANDLE: $(html, props)
// 解析$(HTML, props)格式
// 如果正则rsingleTag验证"match[1]"是否是一个单独的标签,且context是一个纯粹的对象条件成立。循环这个json对象,并判断json里的属性是否是jq自带的方法,如果是,则直接调用方法,否则,用jq的attr方法为这个标签加一个“match”属性。
if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) {
for ( match in context ) {
// Properties of context are called as methods if possible
if ( isFunction( this[ match ] ) ) {
this[ match ]( context[ match ] );
// ...and otherwise set as attributes
} else {
this.attr( match, context[ match ] );
}
}
}
// 返回当前对象
return this;
// HANDLE: $(#id)
// 判断字符串是ID且未指定参数
} else {
//使用getElementById()方法查找含有Id属性的DOM元素
elem = document.getElementById( match[ 2 ] );
//如果DOM元素存在,设置第一个元素,属性length,并返回当前对象。
if ( elem ) {
// Inject the element directly into the jQuery object
this[ 0 ] = elem;
this.length = 1;
}
// 返回当前对象
return this;
}
}
// HANDLE: $(expr, $(...))
// 判断字符串是选择器表达式
else if ( !context || context.jquery ) {
// 如果没有指定上下文,则执行root.find(selector);如果指定了上下文,且上下文是jQuery对象,则执行context.find(selector);
return ( context || root ).find( selector );
// HANDLE: $(expr, context)
// (which is just equivalent to: $(context).find(expr)
}
// 如果制定了上下文,但上下文不是jQuery对象,则执行this.constructor(context).find(selector),即先创建一个包含了context的jQuery对象,然后在该对象上调用find()方法。
else {
return this.constructor( context ).find( selector );
}
// HANDLE: $(DOMElement)
}
// 如果参数selector含有属性nodeType,则认为selector是DOM元素,设置第一个元素指向该DOM元素、属性length为1,然后返回包含了改DOM元素引用的jQuery对象。:nodeType声明了文档树中节点的类型,例如,element节点的该属性值是1,text节点是3,comment是9,document是9,documentfragment节点是11。
// 参数selector是节点,设置第一个元素、属性length,并返回当前对象。
else if ( selector.nodeType ) {
this[ 0 ] = selector;
this.length = 1;
return this;
}
// HANDLE: $(function)
// Shortcut for document ready
// 参数selector是函数
else if ( isFunction( selector ) ) {
// 判断root.ready是否存在,存在则执行root.ready(selector),否则执行该方法;
return root.ready !== undefined ?
root.ready( selector ) :
// Execute immediately if ready is not present
selector( jQuery );
}
// 参数selector是任意值,如果selector是数组或伪数组(如jQuery对象),则都添加到当前jQuery对象中;
// 如果selector是JavaScript对象,则作为第一个元素放入当前jQuery对象中;
// 如果是其他类型的值,则作为第一个元放入当前jQuery对象中。最后返回当前对象。
return jQuery.makeArray( selector, this );
};
// 通过“init.prototype = jQuery.fn;”用jQuery()原型对象覆盖了jQuery.fn.init()的原型对象。
init.prototype = jQuery.fn;
// Initialize central reference
// 初始化rootjQuery
rootjQuery = jQuery( document );