jQuery.prototype.init选择器构造函数源码思路分析

作者:异步漫步时间:2026-10-27 07:59:27浏览:172

本文深入分析jQuery.prototype.init选择器构造函数的源码逻辑,详细拆解其处理不同参数类型(如DOM元素、HTML字符串、ID选择器、类选择器及函数)时的内部实现机制。通过对比jQuery 1.8.3版本代码,阐明jQuery对象在创建过程中对context、selector、length等属性的赋值规则,以及针对IE兼容性和特殊语法的处理策略,帮助开发者理解jQuery核心框架的工作原理。

一、源码思路分析总结

jQuery.prototype.init源码解析

概要:


jQuery的核心设计理念可概括为“查询和操作DOM”。本文重点剖析jQuery.prototype.init选择器构造函数,解析其如何处理选择器函数接收的各类参数。


该函数接收的参数与jQuery()或$()执行函数中的参数一致。建议先阅读浅析jQuery基础框架一文,了解基础架构后再深入此源码分析。


思路分析:


以下是几种常见的jQuery DOM查询用法,每种情况均返回一个选择器实例(通常称为jQuery对象,本质上是一个类数组的nodeList对象),该对象包含查询到的DOM节点:


1、处理 $(""), $(null), $(undefined), $(false)


当参数为上述非法值时,生成的jQuery对象不包含任何DOM节点。


2、处理 $(DOMElement)


若参数为DOM节点元素,jQuery对象将包含该节点,并分别设置context属性为参数节点元素、length属性为1,同时支持通过[]索引访问jQuery对象中的DOM节点。


例2.1:


DOM元素与HTML字符串处理

代码如下:

 
var obj = document.getElementById('container'),
jq = $(obj);
console.log(jq.length); //1
console.log(jq.context); //obj
console.log(jq.[0]); //obj

3、处理$(HTML字符串)


若第一个参数为HTML字符串,jQuery对象将包含由jQuery.clean函数创建的文档碎片(fragment)中的childNodes节点。


例3.1:


代码如下:

 
var jqHTML = $('<h1>文章标题</h1><p>内容</p>');
console.log(jqHTML); //[<h1>,<p>];

若第一个参数(HTML字符串)为空的单标签,且第二个参数context为非空纯对象:


例3.2:


代码如下:

 
var jqHTML = $('<div></div>', { class: 'css-class', data-name: 'data-val' });
console.log(jqHTML.attr['class']); //css-class
console.log(jqHTML.attr['data-name']); //data-val

4、处理$(#id)


若第一个参数为以#开头的元素ID,jQuery对象将包含唯一拥有该ID的元素节点。


同时,对象会分别增加context属性值为document、selector属性值为参数字符串、length属性为1,并支持通过[]访问jQuery对象中的DOM节点。


例4.1:


ID与类选择器查询机制

代码如下:

 
var jq = $('#container');
console.log(jq.[0]); //包含的dom节点元素
console.log(jq.length); //1
console.log(jq.context); //document
console.log(jq.selector); //container

5、处理$(.className)


若第一个参数为.className,jQuery对象将包含拥有class名为className的标签元素,并增加selector属性值为参数字符串、context属性为document。


实际执行代码为:


代码如下:

 
return jQuery(document).find(className);

6、处理$(.className, context)


若第一个参数是.className,第二个参数是上下文对象(可以是.className字符串,等同于处理$(.className .className),也可以是jQuery对象或DOM节点)。


jQuery对象将包含第二个参数上下文对象中拥有class名为className的后代节点元素,并增加context和selector属性。


实际执行代码为:


代码如下:

 
return jQuery(context).find(className);

例6.1:


html代码:


代码如下:

 
<div class="main">
<h2 class="title">主内容标题</h2>
<p>主标题</p>
</div>
<div class="sub">
<h2 class="title">次内容标题</h2>
<p>次标题</p>
</div>

JavaScript代码:


代码如下:

 
var jq, context;
context = '.sub';
var jq = $('.title', context);
console.log(jq.text()); //次内容标题
console.log(jq.context); //document
console.log(jq.selector); //.sub .title
context = $('.sub');
var jq = $('.title', context);
console.log(jq.text()); //次内容标题
console.log(jq.context); //document
console.log(jq.selector); //.sub .title
context = $('.sub')[0];
var jq = $('.title', context);
console.log(jq.text()); //次内容标题
console.log(jq.context); //className为sub的节点元素
console.log(jq.selector); //.title

7、处理$(fn)


若第一个参数是函数fn,则调用$(document).ready(fn)。


例7.1:


函数与jQuery对象参数处理

代码如下:

 
$(function(e){
console.log('DOMContent is loaded');
}) 
//上面代码等同于:
jQuery(document).ready(function(e) {
console.log('DOMContent is loaded');
}); 

8、处理$(jQuery对象)


若第一个参数是jQuery对象,前文已分析过,在查询DOM时,若参数是#加元素ID,返回的jQuery对象会增加selector属性值为参数字符串、context属性为document。


例8.1:


代码如下:

 
var jq = $('#container');
console.log(jq.selector); // #container
console.log(jq.context); // document

那么当出现$($('#container'))该如何处理呢?同样的,返回的jQuery对象同情况5和6处理的情况一样。


例8.2:


代码如下:

 
var jq2 = $($('#container'));
console.log(jq2.selector); // #container
console.log(jq2.context); // document

二、源码注释分析

[ 基于jQuery1.8.3 ]


jQuery 1.8.3 核心源码剖析

代码如下:

 
var rootjQuery = $(document),
rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/;
jQuery.fn = jQuery.prototype = {
init: function( selector, context, rootjQuery ) {
var match, elem, ret, doc;
// Handle $(""), $(null), $(undefined), $(false)
if ( !selector ) {
return this;
} 
// Handle $(DOMElement)
if ( selector.nodeType ) {
this.context = this[0] = selector;
this.length = 1;
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 ];
} else {
match = rquickExpr.exec( selector );
} 
// Match html or make sure no context is specified for #id
// match[1]不为null,则为html字符串,match[2]不为null,则为元素id
if ( match && (match[1] || !context) ) {
// HANDLE: $(html) -> $(array)
if ( match[1] ) {
context = context instanceof jQuery ? context[0] : context;
doc = ( context && context.nodeType ? context.ownerDocument || context : document );
// scripts is true for back-compat
// selector是由文档碎片中的childnodes组成的数组
selector = jQuery.parseHTML( match[1], doc, true );
// 如果match[1]为空的单标签元素(如:<div><div>)且context为对象字面量
if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
// 如果context对象不为空,则将对象中的属性添加到selector数组中仅有的dom节点中
this.attr.call( selector, context, true );
} 
// merge函数的参数应该为两个数组,目的是将第二个数组中的项合并到第一个数组,而this并不是一个数组,
// this是选择器init构造函数的实例对象,该对象继承jQuery.prototype对象中的length属性(默认为0),因此可以理解好merge函数源码
// 将selector中的dom项合并到this对象中,并返回该对象
return jQuery.merge( this, selector );
// HANDLE: $(#id)
} else {
elem = document.getElementById( match[2] );
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
if ( elem && elem.parentNode ) {
// Handle the case where IE and Opera return items
// by name instead of ID
// ie6,7和Opera存在此bug,当一个标签name和一个标签id值相等时,
// document.getElementById(#id)函数将返回提前出现的标签元素
if ( elem.id !== match[2] ) {
// 如果存在以上Bug,则返回由find函数返回的document文档的后代元素集合
return rootjQuery.find( selector );
} 
// Otherwise, we inject the element directly into the jQuery object
this.length = 1;
this[0] = elem;
} 
this.context = document;
this.selector = selector;
return this;
} 
// HANDLE: $(expr, $(...))
// context不存在或者context为jQuery对象
} else if ( !context || context.jquery ) {
return ( context || rootjQuery ).find( selector );
// HANDLE: $(expr, context)
// (which is just equivalent to: $(context).find(expr)
// context为className或者dom节点元素
} else {
// 等同于jQuery(context).find(selector)
return this.constructor( context ).find( selector );
} 
// 处理$(fn)===$(document).ready(fn)
} else if ( jQuery.isFunction( selector ) ) {
return rootjQuery.ready( selector );
} 
// 处理$(jQuery对象)
if ( selector.selector !== undefined ) {
this.selector = selector.selector;
this.context = selector.context;
} 
// 当第一个参数selector为jQuery对象时,将selector中的dom节点合并到this对象中,并返回this对象
return jQuery.makeArray( selector, this );
} 
} 
作者最新文章