美文网首页我爱编程
jQuery构造函数

jQuery构造函数

作者: 我爱吃烤鸡翅 | 来源:发表于2017-10-25 22:10 被阅读0次

    前言

    本篇文章较长,其中包含了对jQuery源码的分析,如果不感兴趣的同学可以直接查看参数类型部分,感兴趣的同学可以继续阅读下面源码解析,如果内容有误,还望指出。

    背景

    最近的工作需要处理IE8的兼容性问题,但是对于大量的工作任务,使用原生js实现功能,并且兼容IE8是很费时费力的事情,因此为了提高工作效率,JQuery框架便成为了不二选择,于是我便开始学习能提高开发效率的jQuery框架,该专题主要记录学习过程中的问题和总结所需要掌握的知识点,以及对于jQuery源码的分析与理解。文章参考了《jQuery技术内幕》以及网络上其他大牛的博客,旨在分享与记录,如有错误还望指出。

    初识jQuery(构造函数)

    在使用jQuery的过程中,我们首先就要构造jQuery对象,否则无法调用jQuery的方法,但在构造jQuery对象的过程中,我们发现同一个$()方法却可以传入多种不同的参数,于是以下便对参数进行总结以及分析:

    参数类型:

    1.$(element)
    传入参数是一个DOM对象的情况下,则将DOM对象封装成一个jQuery对象并返回。
    2.$(selector[,context])
    selector为jQuery的css选择器,context是个可选参数,其表示查询的上下文及查询范围,如果不指定则表示全局查询。
    3.$(html[,props])
    html为html代码,jQuery会解析html并且使用其创建DOM节点,其中html可分为单独标签以及复杂代码片段,如<p/>或者<p></p>就是单独标签,<p>123</p>则是复杂片段,两种格式的DOM节点创建方式不同。前者是使用document.createElement()方式创建,后者使用innerHTML的方式创建。props则是设置创建的DOM节点的属性。
    4.$(object)
    object为普通javascript对象,则返回一个封装到jQuery中的对象,返回对象可以使用jQuery的方法。
    5.$(callback)
    callback为函数,当传入参数为函数时,会将传入函数绑定到ready时间上,其提前于onload事件,在DOM文本解析完成时便触发,貌似绑定与DOMContentLoaded事件上(猜测,暂时不知道,待后续研究)。
    6.$(jQuery object)
    如果传入一个jQuery对象,则创建该jQuery对象的一个副本并返回,副本与传入的jQuery对象引用完全相同的DOM元素。
    7.$()
    传入空对象,则返回一个空的jQuery对象。

    源码分析:

    通过上述参数分类,我们知道了jQuery的构造函数的强大,接下来我们就通过源码来分析上述不同参数类型在jQuery内部的处理方式,以便与我们进一步加深对于jQuery的理解。
    首先我们从jQuery源码知道jQuery的所有代码都封装在一个自执行函数中,函数代码如下:

    (function(window,undefined){
      ...
    })(window);
    

    上述代码只传入window对象,而内部存在一个undefined参数是为了确保undefined未被重赋值,保证jQuery内部代码的undefined的正确性。(undefined的重赋值问题见此文)

    然后jQuery内部使用一种特别的方式避免了外部使用new方法构造jQuery对象,代码如下:

    jQuery = function( selector, context ) {
        //jQuery.fn实则是jQuery.prototype,init则是jQuery原型上的方法,用于构造jQuery对象
        return new jQuery.fn.init( selector, context, rootjQuery );
    }
    

    那么以上方法就存在一个问题使用init方法构造jQuery对象会导致jQuery的原型方法实例无法使用,因为实例的原型是init方法的原型而init方法上并没有任何方法。于是jQuery采取了一种极其机智的方法处理上述问题,代码如下:

    jQuery.fn.init.prototype = jQuery.fn;
    

    以上代码将jQuery的prototype赋值给init的prototype(注意此时jQuery.fn=jQuery.prototype),于是init方法的实例自然可以使用jQuery对象上的原型方法了。

    --等等,说了这么多压根没提jQuery.fn.init方法的内部机制呀💢,别急,理解了上面的知识更有利于理解init内部机制,接下来就让我们看看jQuery.fn.init的源码吧。

    代码:(解释均在代码注释上)

    function( selector, context, rootjQuery ) {
        //selector可以是DOM对象,undefined,字符串,函数,JQuery对象,普通javaScript对象-有效,其他参数无效
        //context可传入,可不传入,DOM对象,JQuery对象,普通javaScript对象
        //rootJQuery是包含了document对象的JQuery对象,用于document.getElementById()查找失败,selector选择器未指定context或者selector是函数的情况
        
        var match, elem, ret, doc;
        
        //处理$(''),$(null),$(undefined),$(false)
        if ( !selector ) {
            return this;
        }
    
        //处理传入参数是DOMElement的情况。
        if ( selector.nodeType ) {//$(this)等DOM对象在此转换成JQuery对象
            this.context = this[0] = selector;
            this.length = 1;
            return this;
        }
    
        // 处理传入是HTML代码的情况
        if ( typeof selector === "string" ) {
            if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
                //由于IE6~IE8与标准浏览器对charAt方法的支持是一样的。
                //而使用String[]的方法取值在IE6~IE8下会返回undefined(注意必须是String对象而非字面量)
                //处理HTML代码开始是<,结束是>的情况
                match = [ null, selector, null ];
            } else {
                //rquickExpr=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/
                match = rquickExpr.exec( selector );
            }
    
            // Match html or make sure no context is specified for #id
            if ( match && (match[1] || !context) ) {
                //如果match存在,且match[1],或者match[2]且context上下文范围为undefined的情况下执行下列代码
                // 处理: $(html) -> $(array)
                if ( match[1] ) {
                    //如果match[1]表示是创建dom节点的话,先修正context对象
                    context = context instanceof jQuery ? context[0] : context;
                    doc = ( context && context.nodeType ? context.ownerDocument || context : document );
    
                    // scripts is true for back-compat
                    //parseHTML对单一标签内容通过createElement方法创建对象,并返回一个包含该对象的数组。
                    //jQuery将对HTML代码的复杂对象与非复杂对象处理都放在了parseHTML函数中,感兴趣的同学可下去继续研究,我也会在以后对该函数进行分析。
                    selector = jQuery.parseHTML( match[1], doc, true );
                    
                    if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
                    //在context是props属性时,将属性赋值到新生成的DOM元素上
                        this.attr.call( selector, context, true );
                    }
    
                    return jQuery.merge( this, selector );
    
                // 处理: $(#id)
                } else {
                    //查找id对应元素                  
                    elem = document.getElementById( match[2] );
    
                    //检测parentNode属性,因为黑莓4.6会返回已经在文档中不存在的节点
                    if ( elem && elem.parentNode ) {
                        
                        //在IE7和IE6中有可能按照name查找了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;
                }
    
            // 处理: $(selector, $(...))
            } else if ( !context || context.jquery ) {
                return ( context || rootjQuery ).find( selector );
    
            // 处理: $(selector, context)
            // 相当于$(context).find(expr)
            } else {
                return this.constructor( context ).find( selector );
            }
    
        // 处理: $(function)
        } else if ( jQuery.isFunction( selector ) ) {
            return rootjQuery.ready( selector );//如果是一个函数则执行ready
        }
    
        if ( selector.selector !== undefined ) {
            //如果传入对象是JQuery对象
            this.selector = selector.selector;
            this.context = selector.context;
        }
        //在传入参数是非上述类型数据,而是如同Array,Number,JS原生对象的情况下,将直接置入当前jQuery实例中返回,以使上述数据可使用jQuery方法
        return jQuery.makeArray( selector, this );
    }  
    

    上述代码段便是jQuery的init方法内部机制,其中我都加上了解释与自己的理解,希望读者能够阅读并且在浏览器环境下通过开发者工具执行jQuery代码并加入断点,观测代码执行情况以进一步测试代码运行。

    总结:

    jQuery是一个非常棒的函数库,它帮助开发者减轻开发负担,解决兼容问题,提高开发效率,但是作为一个想要在JS方面有所成就的开发者,仅会使用是不够的,我们还要花费时间去学习该框架的内部机制,理解其代码风格,当质疑自己代码写得不好的时候,那就是因为你看的代码不够多,因此学习jQuery源码,可以让我们真正了解到自己与大师之间的差距。以后我也会进一步对jQuery代码进行研究,其中很多个大的模块如AJAX,选择器,DOM操作,事件代理等均是jQuery的经典之处,希望有兴趣的同学也可以自己研究深入。
    行文仓促,如有错误,还望指出。🙏

    相关文章

      网友评论

        本文标题:jQuery构造函数

        本文链接:https://www.haomeiwen.com/subject/yksfpxtx.html