jQuery源码初探(4)

来源:互联网 发布:白板软件下载 编辑:程序博客网 时间:2024/06/10 06:52

我们上一篇聊到了 jQuery 中的无 new 构造,我们发现其真正的初始化操作是放在 new jQuery.fn.init( selector, context );里面的,今天我们就来看看这个 init() 函数的具体内容;

jQuery 的源码中是这么定义的:

    init = jQuery.fn.init = function( selector, context, root ) {            var match, elem;            // HANDLE: $(""), $(null), $(undefined), $(false)            if ( !selector ) {                return this;            }            // Method init() accepts an alternate rootjQuery            // so migrate can support jQuery.sub (gh-2101)            root = root || rootjQuery;            // Handle HTML strings            if ( typeof selector === "string" ) {                if ( selector[ 0 ] === "<" &&                    selector[ 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                if ( match && ( match[ 1 ] || !context ) ) {                    // HANDLE: $(html) -> $(array)                    if ( match[ 1 ] ) {                        context = context instanceof jQuery ? context[ 0 ] : context;                        // Option to run scripts is true for back-compat                        // Intentionally let the error be thrown if parseHTML is not present                        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 ) {                                // 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)                    } else {                        elem = document.getElementById( match[ 2 ] );                        if ( elem ) {                            // Inject the element directly into the jQuery object                            this[ 0 ] = elem;                            this.length = 1;                        }                        return this;                    }                // HANDLE: $(expr, $(...))                } else if ( !context || context.jquery ) {                    return ( context || root ).find( selector );                // HANDLE: $(expr, context)                // (which is just equivalent to: $(context).find(expr)                } else {                    return this.constructor( context ).find( selector );                }            // HANDLE: $(DOMElement)            } else if ( selector.nodeType ) {                this[ 0 ] = selector;                this.length = 1;                return this;            // HANDLE: $(function)            // Shortcut for document ready            } 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 );        };

我们来分析一下这一段代码,
首先定义了 match 和 elem 两个变量, 接着进行一个判断,这个判断作用注释也写的很清楚,主要是过滤$(""), $(null), $(undefined), $(false)这种不符合规则的选择,如果成立,就直接返回(此时返回的是空的 jq 对象)~
root = root || rootjQuery; 接着对 root 赋值 , 因为源码中有 rootjQuery = jQuery( document ); 这么一句, 所以此时的 root 也就是 document 对象。

如果我们输入合法,那么就走下面的判断,判断字符串里面的内容比较多,我们先看字符串以外的部分,
第一个判断就是
处理文档类型
这个判断主要是针对 DOM 元素 , 比如 我们平时用的 $(document) , $(body) 这种类型的选择 ,因为DOM 元素 nodeType节点类型一定是存在的,利用是否有nodeType属性来判断是否是DOM元素 ,如果正确就赋给 this 对象 并将其返回。
如果还不是 DOM 元素呢 , 那么就会 进入下面这个判断
处理函数
这个判断是用来判断,如果我们传入的是一个 function 类型的时候,其主要是处理我们 平时这么写的一种文档加载完成的方式——$(function(){}),其实这种写法最终还是走到了 $(document).ready(function(){})里面来,如果 ready 函数存在 , 那么直接放在 $(document).ready() 中等待执行,如果没有 ready 函数那么直接执行(一般引入了 jQuery 的话 ready函数是存在的);
如果以上条件都不是 , 那么传入的就是一个 DOM 类数组,也就是$(document.getElementsByTagName('li')) 类似这种形式 , 直接合并到当前 jQuery 对象将其返回, 对应源码中最后 return jQuery.makeArray( selector, this );

ok,我们接下来仔细看看 jQuery 处理各种各样的字符串 类型 ,
处理标签
先来一个判断,判断当前字符串 是不是 符合 第一个字符是 ‘<’ , 最后一个字符是 ‘>’ , 而且字符串总长度大于等于 3 , 也就是处理我们常用的 HTML 单标签 类型($('<div>') , $('<a>') 等) , 我们将标签 放到 match 这个变量的 第二个 位置上;
如果不符合条件,就将 rquickExpr.exec( selector ); 的执行结果赋给 match , 那我们先看看这个 rquickExpr 正则表达式,源码中是这么定义的

    rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/

这个正则是匹配 <tag>text, <tag>text</tag>text#id 文本, 也就是$("htmlText")$("#id")用法 的字符(ps:这个正则还防止了 XSS 通过哈希注入, 感兴趣的同学可以去了解一下 ~)
那么正则返回什么呢,我们做个测试,
rquickExpr正则
我们发现使用exec方法后,如果匹配到了,则应该返回一个长度为3,包括 index 和 input 属性的数组。
那么match[0]是整体匹配的数组,match[1]是匹配的<tag>标签(如果有),match[2]匹配#id(如果有),并且 match[1],match[2]只能有一个被捕获~

如果匹配的字符串是 htmlText 标签 ,也就是 $('<div>123</div>') 或者 $('<div>123') 这种情况的话,那么 jQuery 会根据html生成dom元素,返回 jQuery 对象;

    context = context instanceof jQuery ? context[0] : context;

如果context是jQuery对象,那么就采用 context[0] 将其转为js原生对象 , 在这里context期待传入的就是document 或者 $(document);
接着执行调用了 jQuery.parseHTML 方法,该方法就是把 htmlString 转为 dom 数组

    jQuery.merge( this, jQuery.parseHTML(        match[ 1 ],        context && context.nodeType ? context.ownerDocument || context : document,        true    ) );

还调用了jQuery.merge( first , second ),这个方法接收两个”类数组”参数,并把第二个数组追加到第一个数组尾部,会改变第一个数组,所以这段代买就是就是把 htmlString 转为 dom 数组并追加到this的对象中。(这些方法我们后面陆续介绍,先了解其作用~)

    // HANDLE: $(html, props)    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 ] );            }        }    }

这一段是处理 jQuery( html, attributes ) 这种用法 , 第一个参数必须是单标签,第二个参数是一个普通对象,类似 { html:"hello world", id: "test" } , 把后面对象的属性一一添加到创建的这个DOM节点的属性上,(ps: 注意,这种情况下,会走上面的分支,也就是已经把单标签转为 DOM 并放到 this 对象中了,for 循环中的 match 是 context 对象的属性。如果有match的方法,就会调用 对应的 match 方法
比如{ html : “hello world”, id : “test” },就会调用 this.html(“hello world”) 方法 , 否则按照属性处理)

接着是处理 match[2] 被捕获到的情况,也就是 #id 的情况,直接了当,调用原生获取 id 元素的方法,并存放到 this 对象 返回;
处理id

接下来就是处理复杂选择器的情况,采用find方法处理并返回,
复杂选择器
这里两个else的判断,就是为了确保是 jQuery 对象调用find方法,如果 context不是 jQuery 对象,使用 constructor 构造一个~
(ps:这块是调用Sizzle方法,这是jQuery选择器的核心方法,等到了后面我看懂了再告诉大家吧 ~ 逃 )

2 0
原创粉丝点击