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是如何实现隐式迭代和链式调用的。

posted @ 2012-02-08 14:34  有梦就能实现  阅读(842)  评论(0编辑  收藏  举报