前面一讲总体架构已经介绍了 jQuery 的基本情况,这一章主要来介绍 jQuery 的入口函数 jQuery.fn.init 。
由于这个函数直接和 jQuery() 的参数有关,先来说下能接受什么样的参数。源码中接受 3 个参数:
init: function (selector, context, root) { ... }
jQuery() ,空参数,这个会直接返回一个空的 jQuery 对象, return this 。
jQuery( selector [, context ] ) ,这是一个标准且常用法,selector 表示一个 css 选择器,这个选择器通常是一个字符串,#id 或者 .class 等,context 表示选择范围,即限定作用,可为 DOM,jQuery 对象。
jQuery( element|elements ) ,用于将一个 DOM 对象或 DOM 数组封装成 jQuery 对象。
jQuery( jQuery object|object ) ,会把普通的对象或 jQuery 对象包装在 jQuery 对象中。
jQuery( html [, ownerDocument ] ) ,这个方法用于将 html 字符串先转成 DOM 对象后在生成 jQuery 对象。
jQuery( html, attributes ) ,和上一个方法一样,不过会将 attributes 中的方法和属性绑定到生成的 html DOM 中,比如 class 等。
jQuery( callback ) ,此方法接受一个回掉函数,相当于 window.onload 方法,只是相对于。
jQuery.fn.init介绍完入口,就开始来看源码。
init: function (selector, context, root) { var match, elem; // 处理: $(""), $(null), $(undefined), $(false) if (!selector) { return this; } // rootjQuery = jQuery( document ); root = root || rootjQuery; // 处理 HTML 字符串情况,包括 $("<div>")、$("#id")、$(".class") if (typeof selector === "string") { //此部分拆分,留在后面讲 // HANDLE: $(DOMElement) } else if (selector.nodeType) { this[0] = selector; this.length = 1; return this; // HANDLE: $(function) } else if (jQuery.isFunction(selector)) { return root.ready !== undefined ? root.ready(selector) : // Execute immediately if ready is not present selector(jQuery); } return jQuery.makeArray(selector, this); }
上面有几点需要注意, root = root || rootjQuery; ,这个参数在前面介绍用法的时候,就没有提及,这个表示 document,默认的话是 rootjQuery,而 rootjQuery = jQuery( document )。
可以看出,对于处理 $(DOMElement) ,直接是把 jQuery 当作一个数组,this[0] = DOMElement。其实,这要从 jQuery 的基本构造讲起,我们完成一个 $('div.span') 之后,然后一个 jQuery 对象(this),其中会得到一组(一个)DOM 对象,jQuery 会把这组 DOM 对象当作数组元素添加过来,并给一个 length。后面就像一些链式函数操作的时候,若只能对一个 DOM 操作,比如 width、height,就只对第一个元素操作,若可以对多个 DOM 操作,则会对所有 DOM 进行操作,比如 css()。
jQuery 大题思路如下,这是一个非常简单点实现:
jQuery.prototype = { // 简单点,假设此时 selector 用 querySelectorAll init: function(selector){ var ele = document.querySelectorAll(selector); // 把 this 当作数组,每一项都是 DOM 对象 for(var i = 0; i < ele.length; i++){ this[i] = ele[i]; } this.length = ele.length; return this; }, //css 若只有一个对象,则取其第一个 DOM 对象 //若 css 有两个参数,则对每一个 DOM 对象都设置 css css : function(attr,val){ for(var i = 0; i < this.length; i++){ if(val == undefined){ if(typeof attr === 'object'){ for(var key in attr){ this.css(key, attr[key]); } }else if(typeof attr === 'string'){ return getComputedStyle(this[i])[attr]; } }else{ this[i].style[attr] = val; } } }, }
所以对于 DOMElement 的处理,直接将 DOM 赋值给数组后,return this。
jQuery.makeArray 是一个绑定 数组的函数,和上面的原理一样,后面会谈到。
在介绍下面的内容之前,先来介绍一个 jQuery 中一个识别 Html 字符串的正则表达式,
var rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/; rquickExpr.exec('<div>') //["<div>", "<div>", undefined] rquickExpr.exec('<div></div>') //["<div></div>", "<div></div>", undefined] rquickExpr.exec('#id') //["#id", undefined, "id"] rquickExpr.exec('.class') //null
上面这一系列的正则表达式 exec,只是为了说明 rquickExpr 这个正则表达式执行后的结果,首先,如果匹配到,结果数组的长度是 3,如果匹配到 ‘
‘ 这种 html,数组的第三个元素是 underfined,如果匹配到 #id,数组的第二个元素是 underfined,如果匹配不到,则为 null。
另外还有一个正则表达式:
var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); rsingleTag.test('<div></div>') //true rsingleTag.test('<div ></div>') //true rsingleTag.test('<div class="cl"></div>') //false rsingleTag.test('<div></ddiv>') //false
这个正则表达式主要是对 html 的字符串进行验证,达到不出差错的效果。在这里不多介绍 exec 和正则表达式了。
下面来看下重点的处理 HTMl 字符串的情况:
if (selector[0] === "<" && selector[selector.length - 1] === ">" && selector.length >= 3) { // 这个其实是强行构造了匹配 html 的情况的数组 match = [null, selector, null]; } else { match = rquickExpr.exec(selector); } // macth[1] 限定了 html,!context 对 #id 处理 if (match && (match[1] || !context)) { // HANDLE: $(html) -> $(array) if (match[1]) { //排除 context 是 jQuery 对象情况 context = context instanceof jQuery ? context[0] : context; // jQuery.merge 是专门针对 jQuery 合并数组的方法 // jQuery.parseHTML 是针对 html 字符串转换成 DOM 对象 jQuery.merge(this, jQuery.parseHTML( match[1], context && context.nodeType ? context.ownerDocument || context : document, true)); // HANDLE: $(html, props) if (rsingleTag.test(match[1]) && jQuery.isPlainObject(context)) { for (match in context) { // 此时的 match 非彼时的 match if (jQuery.isFunction(this[match])) { this[match](context[match]); // ...and otherwise set as attributes } else { this.attr(match, context[match]); } } } return this; // 处理 match(1) 为 underfined 但 !context 的情况 } else { elem = document.getElementById(match[2]); if (elem) { // this[0] 返回一个标准的 jQuery 对象 this[0] = elem; this.length = 1; } return this; } // 处理一般的情况,find 实际上上 Sizzle,jQuery 已经将其包括进来,下章详细介绍 // jQuery.find() 为 jQuery 的选择器,性能良好 } else if (!context || context.jquery) { return (context || root).find(selector); // 处理 !context 情况 } else { // 这里 constructor 其实是 指向 jQuery 的 return this.constructor(context).find(selector); }
关于 nodeType,这是 DOM 的一个属性,详情 Node.nodeType MDN 。nodeType 的值一般是一个数字,比如 1 表示 DOM,3 表示文字等,也可以用这个值是否存在来判断 DOM 元素,比如 context.nodeType。