jQuery源码分析之init的详细介绍

init 构造器

由于这个函数直接和 jQuery() 的参数有关,先来说下能接受什么样的参数。

源码中接受 3 个参数:

init: function (selector, context, root) {
 ...
}
  1. jQuery() ,空参数,这个会直接返回一个空的 jQuery 对象,return this。
  2. jQuery( selector [, context ] ) ,这是一个标准且常用法,selector 表示一个 css 选择器,这个选择器通常是一个字符串,#id 或者 .class 等,context 表示选择范围,即限定作用,可为 DOM,jQuery 对象。
  3. jQuery( element|elements ) ,用于将一个 DOM 对象或 DOM 数组封装成 jQuery 对象。
  4. jQuery( jQuery object|object ) ,会把普通的对象或 jQuery 对象包装在 jQuery 对象中。
  5. jQuery( html [, ownerDocument ] ) ,这个方法用于将 html 字符串先转成 DOM 对象后在生成 jQuery 对象。
  6. jQuery( html, attributes ) ,和上一个方法一样,不过会将 attributes 中的方法和属性绑定到生成的 html DOM 中,比如 class 等。
  7. jQuery( callback ) ,此方法接受一个回掉函数,相当于 window.onload 方法,只是相对于。

介绍完入口,就开始来看源码。

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,如果匹配到 <div> 这种 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。

整个 init 函数等构造逻辑,非常清晰,比如 (selector, context, root) 三个参数,分别表示选择的内容,可能存在的的限制对象或 Object,而 root 则默认的 jQuery(document) 。依旧采用 jQuery 常用的方式,对每一个变量的处理都非常的谨慎。

如果仔细看上面两部分源代码,我自己也加了注释,应该可以把整个过程给弄懂。

find 函数实际上是 Sizzle,已经单独出来一个项目,被在 jQuery 中直接使用,将在下章介绍 jQuery 中的 Sizzle 选择器。通过源码,可以发现:

jQuery.find = function Sizzle(){...}
jQuery.fn.find = function(selector){
 ...
 //引用 jQuery.find
 jQuery.find()
 ...
}

衍生的函数

init 函数仍然调用了不少 jQuery 或 jQuery.fn 的函数,下面来逐个分析。

jQuery.merge

这个函数通过名字,就知道它是用来干什么的,合并。

jQuery.merge = function (first, second) {
 var len = +second.length,
 j = 0,
 i = first.length;

 for (; j < len; j++) {
 first[i++] = second[j];
 }

 first.length = i;

 return first;
}

这样子就可以对类似于数组且有 length 参数的类型进行合并,我感觉主要还是为了方便对 jQuery 对象的合并,因为 jQuery 对象就是有 length 的。

jQuery.parseHTML

这个函数也非常有意思,就是将一串 HTML 字符串转成 DOM 对象。

首先函数接受三个参数,第一个参数 data 即为 html 字符串,第二个参数是 document 对象,但要考虑到浏览器的兼容性,第三个参数 keepScripts 是为了删除节点里所有的 script tags,但在 parseHTML 里面没有体现,主要还是给 buildFragment 当作参数。

总之返回的对象,是一个 DOM 数组或空数组。

jQuery.parseHTML = function (data, context, keepScripts) {
 if (typeof data !== "string") {
 return [];
 }
 // 平移参数
 if (typeof context === "boolean") {
 keepScripts = context;
 context = false;
 }

 var base, parsed, scripts;

 if (!context) {

 // 下面这段话的意思就是在 context 缺失的情况下,建立一个 document 对象
 if (support.createHTMLDocument) {
 context = document.implementation.createHTMLDocument("");
 base = context.createElement("base");
 base.href = document.location.href;
 context.head.appendChild(base);
 } else {
 context = document;
 }
 }
 // 用来解析 parsed,比如对 "<div></div>" 的处理结果 parsed:["<div></div>", "div"]
 // parsed[1] = "div"
 parsed = rsingleTag.exec(data);
 scripts = !keepScripts && [];

 // Single tag
 if (parsed) {
 return [context.createElement(parsed[1])];
 }
 // 见下方解释
 parsed = buildFragment([data], context, scripts);

 if (scripts && scripts.length) {
 jQuery(scripts).remove();
 }

 return jQuery.merge([], parsed.childNodes);
}

buildFragment 函数主要是用来建立一个包含子节点的 fragment 对象,用于频发操作的添加删除节点。parsed = buildFragment([data], context, scripts);建立好一个 fragment 对象,用 parsed.childNodes 来获取这些 data 对应的 HTML。

jQueyr.makeArray

jQuery 里面的函数调用,真的是一层接一层,虽然有时候光靠函数名,就能知道这函数的作用,但其中思考之逻辑还是挺参考意义的。

jQuery.makeArray = function (arr, results) {
 var ret = results || [];

 if (arr != null) {
 if (isArrayLike(Object(arr))) {
 jQuery.merge(ret, typeof arr === "string" ? [arr] : arr);
 } else {
 push.call(ret, arr);
 }
 }

 return ret;
}

makeArray 把左边的数组或字符串并入到右边的数组或一个新数组,其中又间接的引用 jQuery.merge 函数。

接下来是着 isArrayLike 函数,可能需要考虑多方面的因素,比如兼容浏览器等,就有了下面这一长串:

function isArrayLike(obj) {

 // Support: real iOS 8.2 only (not reproducible in simulator)
 // `in` check used to prevent JIT error (gh-2145)
 // hasOwn isn't used here due to false negatives
 // regarding Nodelist length in IE
 var length = !!obj && "length" in obj && obj.length,
 type = jQuery.type(obj);

 if (type === "function" || jQuery.isWindow(obj)) {
 return false;
 }

 return type === "array" || length === 0 || typeof length === "number" && length > 0 && (length - 1) in obj;
}

总结

这篇文章主要介绍了jQuery中比较重要的入口函数,之后将会继续讲解 Sizzle,jQuery 中的选择器。感兴趣的朋友们请继续关注我们,谢谢大家的支持。

(0)

相关推荐

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

    一.源码思路分析总结 概要: jQuery的核心思想可以简单概括为"查询和操作dom",今天主要是分析一下jQuery.prototype.init选择器构造函数,处理选择器函数中的参数: 这个函数的参数就是jQuery()===$()执行函数中的参数,可以先看我之前写的浅析jQuery基础框架一文,了解基础框架后,再看此文. 思路分析: 以下是几种jQuery的使用情况(用于查询dom),每种情况都返回一个选择器实例(习惯称jQuery对象(一个nodeList对象),该对象包含查询

  • jQuery学习笔记之jQuery.fn.init()的参数分析

    从return new jQuery.fn.init( selector, context, rootjQuery )中可以看出参数selector和context是来自我们在调用jQuery方法时传过来的.那么selector和context都有哪些可能. 对于表格中的4~9行中的可能做具体分析. 如果selector是字符串,则首先检测是html代码还是#id.126行的if语句:以"<"开头,以">"结尾,且长度>=3.则假设额这个是HTML

  • jQuery构造函数init参数分析

    在我的上一篇随笔里面分析了jQuery的构造函数,jQuery对象中有一个原型方法init才是是真正的构造函数,通过init的原型对象跟jQuery的原型对象保持引用关系使得init的实例可以正常调用jQuery的原型方法,就好像是jQuery的实例一样.下面就来看看init这个幕后的构造函数是怎么写的: init: function( selector, context, rootjQuery ) { ... } 可以看到这个方法接受3个参数,其前两个参数是jQuery方法传递过来的 var

  • jQuery构造函数init参数分析续

    如果selector是其他字符串情况就比较多了比较复杂了 // Handle HTML strings if ( typeof selector === "string" ) {...} 开始分不同的情况处理 // Are we dealing with HTML string or an ID? if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 )

  • jQuery源码分析之init的详细介绍

    init 构造器 由于这个函数直接和 jQuery() 的参数有关,先来说下能接受什么样的参数. 源码中接受 3 个参数: init: function (selector, context, root) { ... } jQuery() ,空参数,这个会直接返回一个空的 jQuery 对象,return this. jQuery( selector [, context ] ) ,这是一个标准且常用法,selector 表示一个 css 选择器,这个选择器通常是一个字符串,#id 或者 .cl

  • muduo源码分析之TcpServer模块详细介绍

    这次我们开始muduo源代码的实际编写,首先我们知道muduo是LT模式,Reactor模式,下图为Reactor模式的流程图[来源1] 然后我们来看下muduo的整体架构[来源1] 首先muduo有一个主反应堆mainReactor以及几个子反应堆subReactor,其中子反应堆的个数由用户使用setThreadNum函数设置,mainReactor中主要有一个Acceptor,当用户建立新的连接的时候,Acceptor会将connfd和对应的事件打包为一个channel然后采用轮询的算法,

  • jQuery源码分析-03构造jQuery对象-工具函数

    作者:nuysoft/高云 QQ:47214707 EMail:nuysoft@gmail.com 声明:本文为原创文章,如需转载,请注明来源并保留原文链接. 读读写写,不对的地方请告诉我,多多交流共同进步,本章的的PDF等本章写完了发布. jQuery源码分析系列的目录请查看 http://nuysoft.iteye.com/blog/1177451,想系统的好好写写,目前还是从我感兴趣的部分开始,如果大家有对哪个模块感兴趣的,建议优先分析的,可以告诉我,一起学习. 3.4 其他静态工具函数

  • jQuery源码分析之jQuery.fn.each与jQuery.each用法

    本文实例讲述了jQuery源码分析之jQuery.fn.each与jQuery.each用法.分享给大家供大家参考.具体分析如下: 先上例子,下面代码的作用是:对每个选中的div元素,都给它们添加一个red类 复制代码 代码如下: $('div').each(function(index, elem){       $(this).addClass('red'); } }); 上面用的的.each,即jQuery.fn.each,其内部是通过jQuery.each实现的 复制代码 代码如下: j

  • jQuery源码分析-01总体架构分析

    1. 总体架构 1.1 自调用匿名函数 self-invoking anonymous function 打开jQuery源码,首先你会看到这样的代码结构: 复制代码 代码如下: (function( window, undefined ) { // jquery code })(window); 1. 这是一个自调用匿名函数.什么东东呢?在第一个括号内,创建一个匿名函数:第二个括号,立即执行 2. 为什么要创建这样一个"自调用匿名函数"呢? 通过定义一个匿名函数,创建了一个"

  • jQuery源码分析之Callbacks详解

    代码的本质突出顺序.有序这一概念,尤其在javascript--毕竟javascript是单线程引擎. javascript拥有函数式编程的特性,而又因为javascript单线程引擎,我们的函数总是需要有序的执行.优秀代码常常 把函数切割成各自的模块,然后在某一特定条件下执行,既然这些函数是有序的执行,那么我们为什么不编写一个统一管理的对象,来帮助我们管理这些函数--于是,Callbacks(回调函数)诞生. 什么是Callbacks javascript中充斥着函数编程,例如最简单的wind

  • jQuery源码分析之sizzle选择器详解

    前言 Sizzle 原本是 jQuery 中用来当作 DOM 选择器的,后来被 John Resig 单独分离出去,成为一个单独的项目,可以直接导入到项目中使用. 点击这里下:jquery/sizzle. 本来我们使用 jQuery 当作选择器,选定一些 #id 或 .class,使用 document.getElementById 或 document.getElemensByClassName 就可以很快锁定 DOM 所在的位置,然后返回给 jQuery 当作对象.但有时候会碰到一些比较复杂

  • jQuery源码分析-03构造jQuery对象-源码结构和核心函数

    作者:nuysoft/高云 QQ:47214707 EMail:nuysoft@gmail.com 毕竟是边读边写,不对的地方请告诉我,多多交流共同进步.本章还未写完,完了会提交PDF. 前记: 想系统的好好写写,但是会先从感兴趣的部分开始. 近期有读者把PDF传到了百度文库上,首先感谢转载和传播,但是据为已有并设置了挺高的财富值才能下载就不好了,以后我整理好了会传到文库上.请体谅一下. 3. 构造jQuery对象 3.1 源码结构 先看看总体结构,再做分解: 复制代码 代码如下: (funct

  • jQuery源码分析-04 选择器-Sizzle-工作原理分析

    作者:nuysoft/高云 QQ:47214707 EMail:nuysoft@gmail.com 声明:本文为原创文章,如需转载,请注明来源并保留原文链接. 在分析Sizzle源码之前,先整理一下选择器的工作原理 先明确一些选择器中用到的名词,后边阅读时不会有歧义: 选择器表达式: "div > p" 块表达式: "div" "p" 并列选择器表达式: "div, p" 块分割器: Sizzle中的chunker正则,

  • jQuery源码分析-05异步队列 Deferred 使用介绍

    5. 异步队列 Deferred 5.1 概述 异步队列是一个链式对象,增强对回调函数的管理和调用,用于处理异步任务. 异步队列有三种状态:初始化(unresolved),成功(resolved),失败(rejected). 执行哪些回调函数依赖于状态. 状态变为成功(resolved)或失败(rejected)后,将保持不变. 回调函数的绑定可以是同步,也可以是异步的,即可以在任何时候绑定. (本节中的 绑定 注册 增加 具有相同的含义) 5.2 关键方法 先看看jQuery. Deferre

随机推荐