jQuery源码之构造jQuery对象——jQuery.fn.init(selector, context, root)

落花浮王杯 提交于 2020-01-31 16:00:51
jQuery.fn.init()负责解析参数selector和context的类型,并执行相应的逻辑,最后返回jQuery.fn.init()的实例。
支持12种类型,如下所示:
  1. selectort可以转换成false,示例:$(""), $(null), $(undefined), $(false)。
  2. selectort为DOM元素,示例:$(DOMElement)。
  3. selectort为字符串,具体为"body",示例:$('body')。
  4. selectort为字符串,具体为”单独标签“,示例:$('<div>')。
  5. selectort为字符串,具体为”复杂HTML代码“,示例:$('<div>abc</div>')。
  6. selectort为字符串,具体为”#id“,context为undefined,示例:$('#id')。
  7. selectort为字符串,具体为”选择器表达式“,context为undefined,示例:$('div p')。
  8. selectort为字符串,具体为”选择器表达式“,context为jQuery对象,示例:$('div p',$('#id'))。
  9. selectort为字符串,具体为”选择器表达式“,context为DOM元素,示例:$('div p',this)。
  10. selectort为函数,示例:$(function(){})。
  11. selectort为jQuery,示例:$($('div'))。
  12. selectort为其他任意类型的值,示例:$([123])

var rootjQuery,
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,
// rquickExpr包含两个分组,一个匹配HTML、一个匹配ID。
init = jQuery.fn.init = function( selector, context, root ) {
    //参数selector可以是任意类型的值,但只有undefined、DOM元素、
    //字符串、函数、jQuery对象、普通JavaScript对象这几种类型是有效的,
//其他类型的值也可以接受但没有意义。
  //参数context可以不传,或传入DOM元素、jQuery对象、普通JavaScript对象之一。
  //参数root用于document.getElementById()查找失败、selector是选择器表达式且未指定context、selector是函数三种情况情况。
    var match, elem;

    // HANDLE: $(""), $(null), $(undefined), $(false)
    // 参数selector可以转化为false
    //如果传入的是:空字符串、null、undefined、false,则直接返回this。这时this是空jQuery对象,其属性length为0。
    if ( !selector ) {
        return this;
    }

    // Method init() accepts an alternate rootjQuery
    // so migrate can support jQuery.sub (gh-2101)
    // 重置root:rootjQuery包含了document对象的jQuery对象,以此支持jquery.sub
    root = root || rootjQuery;

    // 参数selector是字符串
    if ( typeof selector === "string" ) {
        // 如果参数selector以“<”开头以“>”结尾,且长度大于3。
//则“假设”这个字符串是HTML片段,跳过正则表达式检查。
//否则就用正则rquickExpr检查字符串是否是ID、标签、类选择器,匹配的结果放到match数组中。
        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 {
            // 把selector进行匹配,如果匹配成功,match数组第一个元素是selector,
           //第二个元素是HTML或者是undefined,
          //第三个元素是ID或者是undefined。
            match = rquickExpr.exec( selector );
        }

        // Match html or make sure no context is specified for #id
        // 判断字符串是否是一个单独的标签
        // 如果match[1]不是undefined,即参数selector是HTML代码,
//或者match[2]不是undefined,即参数selector是#id,
//并且未传入参数context。
//完整版的判断如下“if (match && (match[1] || match[2] && !context)) {}”,为什么省略了对//match[2]的判断?
//因为如果match不是null且match[1]是undefined,那么此时match[2]必然不是undefined,所以对match[2]的判断可以省略。
        if ( match && ( match[ 1 ] || !context ) ) {

            // HANDLE: $(html) -> $(array)
            // 判断字符串是HTML
            if ( match[ 1 ] ) {
                //修正context:“context = context instanceof jQuery ? context[0] : context;”。
//先判断第二个参数的类型在将context赋值成原生的节点。例如输入的是:$('li',document)或$('li',$(document))。   
                context = context instanceof jQuery ? context[ 0 ] : context;

                //判断能否向后兼容:“jQuery.merge(this, jQuery.parseHTML())”。
                // jQuery.parseHTML()用于将HTML字符串解析为对应的DOM节点数组。有三个参数:htmlString,context,keepScripts。HTMLString,string类型,需要解析并转为DOM节点数组的字符串。context,element类型。指定在那个document中创建元素。默认为当前文档的document。keepscript,boolean类型,指定传入的字符串中是否包含脚本,默认为false。
                // 我们传入了三个参数:"match[ 1 ],context && context.nodeType ? context.ownerDocument || context : document,true"。如果context与context的节点存在,使用context或context的owner document,否则用默认参数domcument。
                // jQuery.merge()用于合并两个数组内容到第一个数组。注意这时传入的this是个json对象而不是数组,通过这种方式也能进行合并并返回jQuery想要的json格式。
                jQuery.merge( this, jQuery.parseHTML(
                    match[ 1 ],
                    context && context.nodeType ? context.ownerDocument || context : document,
                    true
                ) );

                // HANDLE: $(html, props)
                // 解析$(HTML, props)格式
                // 如果正则rsingleTag验证"match[1]"是否是一个单独的标签,且context是一个纯粹的对象条件成立。循环这个json对象,并判断json里的属性是否是jq自带的方法,如果是,则直接调用方法,否则,用jq的attr方法为这个标签加一个“match”属性。
                if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) {
                    for ( match in context ) {

                        // Properties of context are called as methods if possible
                        if ( isFunction( this[ match ] ) ) {
                            this[ match ]( context[ match ] );

                        // ...and otherwise set as attributes
                        } else {
                            this.attr( match, context[ match ] );
                        }
                    }
                }
                // 返回当前对象
                return this;

            // HANDLE: $(#id)
            // 判断字符串是ID且未指定参数
            } else {
                //使用getElementById()方法查找含有Id属性的DOM元素 
                elem = document.getElementById( match[ 2 ] );
                //如果DOM元素存在,设置第一个元素,属性length,并返回当前对象。
                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 ) {
            // 如果没有指定上下文,则执行root.find(selector);如果指定了上下文,且上下文是jQuery对象,则执行context.find(selector);
            return ( context || root ).find( selector );

        // HANDLE: $(expr, context)
        // (which is just equivalent to: $(context).find(expr)
        } 
        // 如果制定了上下文,但上下文不是jQuery对象,则执行this.constructor(context).find(selector),即先创建一个包含了context的jQuery对象,然后在该对象上调用find()方法。
        else {
            return this.constructor( context ).find( selector );
        }

    // HANDLE: $(DOMElement)
    } 
    //  如果参数selector含有属性nodeType,则认为selector是DOM元素,设置第一个元素指向该DOM元素、属性length为1,然后返回包含了改DOM元素引用的jQuery对象。:nodeType声明了文档树中节点的类型,例如,element节点的该属性值是1,text节点是3,comment是9,document是9,documentfragment节点是11。
    // 参数selector是节点,设置第一个元素、属性length,并返回当前对象。
    else if ( selector.nodeType ) {
        this[ 0 ] = selector;
        this.length = 1;
        return this;
    } 
    // HANDLE: $(function)
    // Shortcut for document ready
    // 参数selector是函数
    else if ( isFunction( selector ) ) {
        //  判断root.ready是否存在,存在则执行root.ready(selector),否则执行该方法;
        return root.ready !== undefined ?
            root.ready( selector ) :

            // Execute immediately if ready is not present
            selector( jQuery );
    }
    // 参数selector是任意值,如果selector是数组或伪数组(如jQuery对象),则都添加到当前jQuery对象中;
  // 如果selector是JavaScript对象,则作为第一个元素放入当前jQuery对象中;
  // 如果是其他类型的值,则作为第一个元放入当前jQuery对象中。最后返回当前对象。
    return jQuery.makeArray( selector, this );
};
// 通过“init.prototype = jQuery.fn;”用jQuery()原型对象覆盖了jQuery.fn.init()的原型对象。
init.prototype = jQuery.fn;

// Initialize central reference
// 初始化rootjQuery
rootjQuery = jQuery( document );

 

 

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!