jQuery 1.8源码分析 core.js核心模块 jQuery对象的构造分析
本文由发表于5年前 | Javascript | 评论数 1 |  被围观 7,691 views+
1、init方法详解:1.1、init方法中使用的正则表达式:1.2、关于该方法编码涉及的一点技巧:1.3、下面是对这个方法代码的解释:
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的其他模块的,了解整体的架构。

除了文章中有特别说明,均为IT宅原创文章,转载请以链接形式注明出处。
本文链接:http://www.itzhai.com/jquery-1-8-source-code-analysis-structural-analysis-of-core-js-core-module-jquery-object.html
关键字: , ,
arthinking Java技术交流群:280755654,入门群:428693174 more
分享到:
 
2013 1/15
文章评论
    一条评论
  1. 清新博主 2013年03月07日15:29:23  #-49楼 回复 回复

    楼主辛苦了哦

给我留言

有人回复时邮件通知我
Javascript的相关文章
随机文章 本月热门 热评
1 Java多线程FAQ汇总 2012/3/6
2 Linux下sed,awk,grep,cut,find等常用命令介绍 2011/6/11
3 JVM 类加载器介绍及其父亲委托机制 Parent Delegation 2011/11/5
4 MappingException提示Association references unmapped class错误的解决方法 2011/5/26
5 JVM笔记 – 自动内存管理机制(虚拟机性能监控与故障处理工具) 2014/11/26
6 Hibernate一对多关联映射的配置及其级联删除问题 2011/5/26
友情推荐 更多
破博客 文官洗碗安天下,武将打怪定乾坤。多么美好的年代,思之令人泪落。
Mr.5's Life 白天是一名程序员,晚上就是个有抱负的探索者
行知-追寻技术之美 关注大数据,分布式系统
我爱编程 编程成长轨迹
Cynthia's Blog 学习笔记 知识总结 思考感悟
 
猜您喜欢
欢迎关注我的公众号 IT宅
关于IT宅 文章归档

IT宅中的文章除了标题注明转载或有特别说明的文章,均为IT宅的技术知识总结,学习笔记或随笔。如果喜欢,请使用文章下面提供的分享组件。转载请注明出处并加入文章的原链接。 感谢大家的支持。

联系我们:admin@itzhai.com

Theme by arthinking. Copyright © 2011-2015 IT宅.com 保留所有权利.