1、init方法详解:
根据上一篇文章的分析,知道init方法其实就是在使用$() 创建jQuery对象是调用的,简单的说其实就是一个元素选择器,在API文档中可以发现,该方法可以提供如下几个用法:
jQuery([selector,[context]])
概述
这个函数接收一个包含 CSS 选择器的字符串,然后用这个字符串去匹配一组元素。
jQuery 的核心功能都是通过这个函数实现的。 jQuery中的一切都基于这个函数,或者说都是在以某种方式使用这个函数。这个函数最基本的用法就是向它传递一个表达式(通常由 CSS 选择器组成),然后根据这个表达式来查找所有匹配的元素。
默认情况下, 如果没有指定context参数,$()将在当前的 HTML document中查找 DOM 元素;如果指定了 context 参数,如一个 DOM 元素集或 jQuery 对象,那就会在这个 context 中查找。在jQuery 1.3.2以后,其返回的元素顺序等同于在context中出现的先后顺序。
参考文档中 选择器 部分获取更多用于 expression 参数的 CSS 语法的信息。
jQuery(html,[ownerDocument]) 返回值:jQuery
概述
根据提供的原始 HTML 标记字符串,动态创建由 jQuery 对象包装的 DOM 元素。同时设置一系列的属性、事件等。
你可以传递一个手写的 HTML 字符串,或者由某些模板引擎或插件创建的字符串,也可以是通过 AJAX 加载过来的字符串。但是在你创建 input 元素的时会有限制,可以参考第二个示例。当然这个字符串可以包含斜杠 (比如一个图像地址),还有反斜杠。当你创建单个元素时,请使用闭合标签或 XHTML 格式。例如,创建一个 span ,可以用$("<span/>") 或 $("<span></span>") ,但不推荐 $("<span>")。在jQuery 中,这个语法等同于$(document.createElement("span")) 。
在jQuery 1.8中,通过$(html,props), 您可以使用任何jQuery对象的方法或插件。在此之前,你只能使用一个方法名的短名单,并有没有成文的方式添加到列表中。现在并不需要是一个列表,在所有!然而,请注意,这可能会导致你的代码的行为改变,如果插件添加后,有相同的名称作为HTML属性。
jQuery(callback) 返回值:jQuery
概述
$(document).ready()的简写。
允许你绑定一个在DOM文档载入完成后执行的函数。这个函数的作用如同$(document).ready()一样,只不过用这个函数时,需要把页面中所有需要在 DOM 加载完成时执行的$()操作符都包装到其中来。从技术上来说,这个函数是可链接的--但真正以这种方式链接的情况并不多。 你可以在一个页面中使用任意多个$(document).ready事件。参考 ready(Function) 获取更多 ready 事件的信息。
为了阅读这个方法,因为里面用到了一个在core.js变量定义部分定义的正则表达式 rquickExpr,所以我们首先来分析一下这个正则表达式的作用。
1.1、init方法中使用的正则表达式:
rquickExpr = /^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,
这里有两个需要强调的正则,如下,其他的自己可以从API文档中找到对应的解释:
(?:pattern) 匹配pattern但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用或字符“(|)”来组合一个模式的各个部分是很有用。例如“industr(?:y|ies)”就是一个比“industry|industries”更简略的表达式。 x|y 匹配x或y。例如,“z|food”能匹配“z”或“food”。“(z|f)ood”则匹配“zood”或“food”。
分析可以知道,这个正则表达式匹配(<[\w\W]+>)[^>]*或者#([\w-]*))$。
首先看到 (<[\w\W]+>)[^>]* :贪婪模式,最大范围的匹配,例如匹配 <a jksldj >> >>asf12,就是从第一个"<"开始,知道最后一个">",还有后面的非">"字符。
([\w-]*):匹配jQuery中的一个ID,如 #abc
所以这个正则的作用就是匹配传入参数中包含的HTML代码或者元素ID。
1.2、关于该方法编码涉及的一点技巧:
虽然是一个init方法,但是通过各种判断(可以查看代码中HANDLE提示,表示处理了不同的输入请求)以及JS的动态参数特性,为jQuery提供了多种初始化方法。还有一个就是获取到的dom节点的信息都封装到了this数组中,方便循环获取。
1.3、下面是对这个方法代码的解释:
// jQuery对象的初始化方法,实际上就是一个选择器
init: function( selector, context, rootjQuery ) {
var match, elem;
// HANDLE: $(""), $(null), $(undefined), $(false)
if ( !selector ) {
// 传入了空,null,undefined或者false的时候,直接返回this引用,没有包含任何节点
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
// 如果是由"<"和">"包裹的HTML则跳过正则匹配,此时是根据传入的HTML创建一个Dom节点
match = [ null, selector, null ];
} else {
match = rquickExpr.exec( selector );
}
// Match html or make sure no context is specified for #id
if ( match && (match[1] || !context) ) {
// HANDLE: $(html) -> $(array)
if ( match[1] ) {
// 根据传入的HTML创建一个Dom节点
context = context instanceof jQuery ? context[0] : context;
// scripts is true for back-compat
jQuery.merge( this, jQuery.parseHTML(
match[1],
context && context.nodeType ? context.ownerDocument || context : document,
true
) );
// HANDLE: $(html, props)
// 根据传入的 HTML创建Dom节点,并根据传入的context设置prop属性
if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
for ( match in context ) {
// Properties of context are called as methods if possible
if ( jQuery.isFunction( this[ match ] ) ) {
// 如果传入的是函数,则这样赋值,以便可以调用
this[ match ]( context[ match ] );
// ...and otherwise set as attributes
} else {
// 如果传入的是属性,则这样赋值
this.attr( match, context[ match ] );
}
}
}
return this;
// HANDLE: $(#id) 处理传入的是ID的情况
} else {
// 根据匹配到的ID使用JS的 getElementById() 方法进行获取元素
elem = document.getElementById( match[2] );
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
// 6963 bug 处理:处理Blackberry 4.6中返回了一个父节点已经不存在于document中的节点的情况
if ( elem && elem.parentNode ) {
// Handle the case where IE and Opera return items
// by name instead of ID
if ( elem.id !== match[2] ) {
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;
}
// 下面两个if语句都调用了jQuery的 find() 方法,详细实现参考 traversing.js
// HANDLE: $(expr, $(...))
} else if ( !context || context.jquery ) {
return ( context || rootjQuery ).find( selector );
// HANDLE: $(expr, context)
// (which is just equivalent to: $(context).find(expr)
} else {
return this.constructor( context ).find( selector );
}
// HANDLE: $(DOMElement)
// 传入的是节点类型,直接保存到this数组中返回
} else if ( selector.nodeType ) {
this.context = this[0] = selector;
this.length = 1;
return this;
// HANDLE: $(function)
// Shortcut for document ready 相当于 $(document).ready() 的缩写
// 传入的是函数,则通过rootjQuery.ready 方法等待执行
} else if ( jQuery.isFunction( selector ) ) {
return rootjQuery.ready( selector );
}
if ( selector.selector !== undefined ) {
this.selector = selector.selector;
this.context = selector.context;
}
return jQuery.makeArray( selector, this );
},
分析代码可知,init方法做了很多个API的实现,比如传入HTML代码创建节点,或者根据id在全局(jQueryRoot)或者指定的范围Context中进行查找。而在指定范围的情况下,调用了jQuery的find() 方法,下面再打开traversing.js中的该方法分析一下:
当我们给$符传递进一个参数(也可能是多个)时,此时它会根据参数的类型(domElement | string | fn | array)进入不同的流程。首先调用正则匹配看是否为创建dom节点的操作,然后看是否为简单id匹配,这一步也由正则匹配完成,否则进入jQuery.fn.find()函数,我们可以打开traversing.js中的该方法分析一下:
find: function( selector ) {
var i, ret, self;
// 如果传入的不是字符串,即可能是Dom节点或者jQuery对象
if ( typeof selector !== "string" ) {
self = this;
// 把过滤获取到的匹配元素数组push到堆栈中,并返回这个元素数组
return this.pushStack( jQuery( selector ).filter(function() {
for ( i = 0; i < self.length; i++ ) {
if ( jQuery.contains( self[ i ], this ) ) {
return true;
}
}
}) );
}
// 下面是传入的参数selector为字符串的情况
ret = [];
for ( i = 0; i < this.length; i++ ) {
// 这里调用了jQuery.find方法,详细查看 sizzle-jquery.js文件,定义如下 jQuery.find = Sizzle;
// 可见其实现在Sizzle中,可以获取其源代码详细阅读: https://github.com/jquery/sizzle
jQuery.find( selector, this[ i ], ret );
}
// Needed because $( selector, context ) becomes $( context ).find( selector )
ret = this.pushStack( jQuery.unique( ret ) );
ret.selector = ( this.selector ? this.selector + " " : "" ) + selector;
return ret;
},
其中pushStack()函数的作用是创建一个对象数组,把传入的对象数组也合并进去然后添加到一个栈中,并返回该新建的对象数组:
// Take an array of elements and push it onto the stack
// (returning the new matched element set)
pushStack: function( elems ) {
// Build a new jQuery matched element set
var ret = jQuery.merge( this.constructor(), elems );
// Add the old object onto the stack (as a reference)
ret.prevObject = this;
ret.context = this.context;
// Return the newly-formed element set
return ret;
},
我们可以看到里面都使用了Sizzle去进行搜索元素,为了探个究竟,我们可以在jQuery的官网http://api.jquery.com/顶部找到Sizzle项目的链接http://sizzlejs.com/ 正如其介绍的,Sizzle是一个纯JavaScript实现的CSS选择器引擎可以方便的嵌入到各种其他的JS类库中用来充当选择器。github上的源代码码下载:https://github.com/jquery/sizzle 这里我们就先跳过这个,看看jQuery的其他模块的,了解整体的架构。