jQuery构造函数

前言

本篇文章较长,其中包含了对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的经典之处,希望有兴趣的同学也可以自己研究深入。
行文仓促,如有错误,还望指出。🙏

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容

  • 在线阅读 http://interview.poetries.top[http://interview.poetr...
    程序员poetry阅读 114,243评论 24 450
  • 一、样式篇 第1章 初识jQuery (1)环境搭建 进入官方网站获取最新的版本 http://jquery.co...
    凛0_0阅读 3,352评论 0 44
  • 这个星期不知道怎么了,大概是距离毕业越来越近了,实验数据做不出来,四六级也没过,想想工作不知道怎么找,就压力很大,...
    一只被嫌弃的小胖阅读 160评论 0 0
  • TIPS:LAMP(LINUX APACHE MYSQL PHP)版本选择须知php 5.5 开始就不支持wind...
    海龙_Lewis阅读 500评论 0 1
  • jQuery 中, $(document).ready()是什么意思? 在文档加载后激活函数:当 DOM(文档对象...
    mianmiani阅读 241评论 0 0