jQuery 1.6 源码学习(三)——core.js[3]之init方法

不得不说,jQuery现在的版本更新速度大有朝chrome看齐的趋势,看来chrome的版本更新价值观很有影响力,github上已经是 1.7版了,不过官方已发布依然是1.6.1,不过想来这些版本内核现在应该是很稳定了,所以学习的时候也不考虑那么多细节了,直接down最新版来啃!

 

在分析core的基本框架的时候已经弄清了,jQuery的实例对象是通过new init方法来返回的,那么init方法便是jQuery的核心方法了,也是jQuery的对外接口jQuery()(即$())的内部实现,熟悉 jQuery的童鞋应该知道除了在构建对象时封装DOM元素为类数组的jQuery实例对象,jQuery()方法也可以通过几种不同的调用方式达到不同 的效果,接下来看看源代码详细的分析:

init: function( selector, context, rootjQuery ) {

    var match, elem, ret, doc;

    // 分支1 在没有指定选择器的情况下,直接返回本身

    // Handle $(""), $(null), or $(undefined)

    if ( !selector ) {

        return this;

    }

    // 分支2 ,当传递单个DOM元素时,直接将其作为jQuery数组的第一个元素 同样,其context也设置为该元素

    // Handle $(DOMElement)

    if ( selector.nodeType ) {

        this.context = this[0] = selector;

        this.length = 1;

        return this;

    }

    // 分支3 因为body元素在一个页面只有一个 因此优化body 访问,context此时设置为document

    // The body element only exists once, optimize finding it

    if ( selector === "body" && !context && document.body ) {

        this.context = document;

        this[0] = document.body;

        this.selector = selector;

        this.length = 1;

        return this;

    }

    // 分支4 处理HTML字符串,此时又分成如下两种情况:

    // 1 : 形如<a></a>的html代码

    // 2 : 形如#xxx的ID选择器

    // Handle HTML strings

    if ( typeof selector === "string" ) {

        // 此处为一个优化,即直接判断第一个字符和最后一个字符是不是<>对已经总字符数大于3个,

        // 在这种条件下假设HTML字符串为正确的,因此略去了正则匹配

        // Are we dealing with HTML string or an ID?

        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

            match = [ null, selector, null ];

 

        } else {

            // 此正则表达式用来匹配HTML字符串或者ID(#xxx)选择器

            // 且该表达式并不完整验证字符串是否正确

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

            match = quickExpr.exec( selector );

        }

        // 如果为形如"#xxx"的ID选择器,捕获组1则为undefined(即未匹配html字符串成功),

        // context的指定则会导致调用find方法

        // Verify a match, and that no context was specified for #id

        if ( match && (match[1] || !context) ) {

            // 此时处理html代码

            // HANDLE: $(html) -> $(array)

            if ( match[1] ) {

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

                doc = (context ? context.ownerDocument || context : document);

                // 处理单个标签情况, 形如 $("<div>") 或 $("<a></a>")

                // 注: rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/

                // If a single string is passed in and it's a single tag

                // just do a createElement and skip the rest

                ret = rsingleTag.exec( selector );

 

                if ( ret ) {

                    if ( jQuery.isPlainObject( context ) ) {

                        selector = [ document.createElement( ret[1] ) ];

                        // 现在attr方法和css方法在底层调用了access方法,此方法的分析先搁置一下,

                        // 在创建HTML代码时,context用来为创建的对象设置其指定的属性

                        jQuery.fn.attr.call( selector, context, true );

 

                    } else {

                        selector = [ doc.createElement( ret[1] ) ];

                    }

 

                } else {

                    //非单个标签时将使用buildFragmenty来创建文档片段,

                    // 该方法位于manipulation.js中,属于内部方法,以后分析

                    ret = jQuery.buildFragment( [ match[1] ], [ doc ] );

                    //clone依然位于manipulation.js中,深度复制一个对象

                    // 此处将selector设置为文档片段的子节点

                    selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes;

                }

                //merge方法用于合并两个数组,在这里会将我们创建的文档片段或者元素合并到this中(即jQuery实例对象中)

                return jQuery.merge( this, selector );

            // HANDLE: $("#id")

            } else {

                // 捕获组2是id值

                elem = document.getElementById( match[2] );

                // 一个bug,参见 http://bugs.jquery.com/ticket/6963

                // Check parentNode to catch when Blackberry 4.6 returns

                // nodes that are no longer in the document #6963

                if ( elem && elem.parentNode ) {

                    // IE和opera返回name而不是ID,此时将调用find方法查找元素

                    // Handle the case where IE and Opera return items

                    // by name instead of ID

                    if ( elem.id !== match[2] ) {

                        return rootjQuery.find( selector );

                    }

                    // 否则直接将DOM元素插入到jQuery实例对象中

                    // Otherwise, we inject the element directly into the jQuery object

                    this.length = 1;

                    this[0] = elem;

                }

 

                this.context = document;

                this.selector = selector;

                return this;

            }

 

        // HANDLE: $(expr, $(...))

        // 处理类似于$("div#div1",$("div#div2"))这种情况时,将递归查找,等价于$("divdiv2").find("div#div1");

        } else if ( !context || context.jquery ) {

            return (context || rootjQuery).find( selector );

 

        // 这种情况是我们处理时最普遍的情况,依然将调用find方法,在选择器代码中再做分析

        // HANDLE: $(expr, context)

        // (which is just equivalent to: $(context).find(expr)

        } else {

            return this.constructor( context ).find( selector );

        }

 

    // HANDLE: $(function)

    // Shortcut for document ready

    // 分支5 处理ready方法,$(fn)的情况

    } else if ( jQuery.isFunction( selector ) ) {

        return rootjQuery.ready( selector );

    }

    //防止$($("xxx"))这种情况处理时发生异常

    if (selector.selector !== undefined) {

        this.selector = selector.selector;

        this.context = selector.context;

    }

    //将jQuery类数组转化为一个真正的数组([xx,xx])

    return jQuery.makeArray( selector, this );

},

 如代码中注释所言,jQuery对象就是这样一点点构建起来的,当然作为选择器的核心方法find留到后面再做分析,这次的分析先告一段落,下次将会看看jQuery是如何实现隐式迭代和链式调用的。

你可能感兴趣的:(jquery)