Jquery 万行代码解析之路(二)—jquery构造函数init

本文继续探讨jQuery的实现细节,重点关注jQuery的核心函数`init`。上文提到,`jQuery()`函数通过返回`new jQuery.fn.init()`对象实现不使用`new`关键字也能调用jQuery方法。`init`函数主要负责根据传入的选择器执行相应的操作。文章将详细解释`init`函数内部的工作原理,并预告下篇文章将讨论`jQuery.makeArray`函数。

嗯,先回顾一下上篇文章讲的内容,上篇讲了jquery中的()jQuery()jQuerynew()函数返回一个new jQuery.fn.init()这个对象,而这个对象的原型即jQuery.fn.init.prototype使之等于jQuery.prototype,从而达到非递归构造的同时(解释可见上一篇)又能返回具有jQuery方法的对象。
但上篇中,我并没有详细的讲init函数内部是做了什么事,只是说是一个基于传进来的选择器参数进行了一些计算,下边我们来详细的看一看init函数干了什么。
鉴于让代码完整一些的原因,我把init的所有代码都贴在这里,然后在代码里写注释来解释吧,可能会有点长(init代码在jQuery3.1.0的2897行左右)

// A central reference to the root jQuery(document)
//一个核心jQuery的引用,这里先不讲。
var rootjQuery,

    // A simple way to check for HTML strings
    // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
    // Strict HTML recognition (#11290: must start with <)
    // Shortcut simple #id case for speed
    rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,
    //注意这个正则表达式非常重要,详细的解释一下,中间有个|在正则中代表选择,即要么是满足左边的表达式,要么是满足右边的表达式我们拆开看
    //^(?:\s*(<[\w\W]+>)[^>]*
    //这里的(?:)代表分组并不记录(这里不普及正则的知识了),^代表开头
    //\s*代表任意多个空白字符(空格、回车、制表等符号都算)
    //然后<[\w\W]+>代表一个html标签,\w代表任意一个字母或数字或下划线,\W和\w相反,外边加中括号代表二者都可以,+号代表一个或多个,至少有一个,这样的话就能组成一个html标签
    //[^>]*这个代表除了>符号以外的任何字符任意多个
    //上边这半边的分组是代表这个选择器selector是一段html代码
    //#([\w-]+))$注意前边有个#是id选择器的开始后边括号里匹配任意一个字母或数字或下划线并且和横杠-一次或多次$代表结尾
    //总结的来说,这个正则是为了匹配传进来的selector是不是一个标签或者一个id

    init = jQuery.fn.init = function( selector, context, root ) {
        var match, elem;

        // HANDLE: $(""), $(null), $(undefined), $(false)
        if ( !selector ) {
            //并没有选择器参数,即调用方式为$()则直接返回this,即new出的init对象
            return this;
        }

        // Method init() accepts an alternate rootjQuery
        // so migrate can support jQuery.sub (gh-2101)
        //这个先不看
        root = root || rootjQuery;

        // Handle HTML strings
        if ( typeof selector === "string" ) {
        //如果选择器是一个字符型的变量,首先判断是否直接是一个标签
            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 {
                //正则验证
                match = rquickExpr.exec( selector );
            }

            // Match html or make sure no context is specified for #id
            //判断这个selector是个html代码并且确认对于id选择器没有上下文(context)对象
            if ( match && ( match[ 1 ] || !context ) ) {

                // HANDLE: $(html) -> $(array)
                //处理方式一:将形如$(html)的代码转换成$(array)的对象返回
                if ( match[ 1 ] ) {
                    context = context instanceof jQuery ? context[ 0 ] : context;

                    // Option to run scripts is true for back-compat
                    // Intentionally let the error be thrown if parseHTML is not present
                    jQuery.merge( this, jQuery.parseHTML(
                        match[ 1 ],
                        context && context.nodeType ? context.ownerDocument || context : document,
                        true
                    ) );

                    // HANDLE: $(html, props)
                    if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) {
                        for ( match in context ) {

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

                            // ...and otherwise set as attributes
                            } else {
                                this.attr( match, context[ match ] );
                            }
                        }
                    }

                    return this;

                // HANDLE: $(#id)
                //处理方式二,id选择器
                } else {
                    //直接使用document对象获取id为match即正则筛选出来的id所对应的对象
                    elem = document.getElementById( match[ 2 ] );

                    if ( elem ) {
                        // Inject the element directly into the jQuery object
                        //如果对象存在,将它注入到这个jQuery对象中去
                        this[ 0 ] = elem;
                        this.length = 1;
                    }
                    //返回
                    return this;
                }

            // HANDLE: $(expr, $(...))
            //处理方式三:若存在context上下文对象并且context是个jquery对象
            } else if ( !context || context.jquery ) {
                //则直接该jquery对象中寻找该对象,这里的find函数的selector形式和init函数的selector是一样的,都支持很多方式
                return ( context || root ).find( selector );

            // HANDLE: $(expr, context)
            // (which is just equivalent to: $(context).find(expr)
            } else {
                //处理方式四:context也是一个选择器的情况
                return this.constructor( context ).find( selector );
            }

        // HANDLE: $(DOMElement)
        //处理方式五:传进来的selector直接是一个页面上的元素对象
        } else if ( selector.nodeType ) {
            //直接将对象注入到jQuery返回
            this[ 0 ] = selector;
            this.length = 1;
            return this;

        // HANDLE: $(function)
        // Shortcut for document ready
        //处理方式六:selector是一个函数,这个就是为了简化document.ready函数的。
        } else if ( jQuery.isFunction( selector ) ) {
            return root.ready !== undefined ?
                root.ready( selector ) :

                // Execute immediately if ready is not present
                selector( jQuery );
        }
        //处理方式七:最后来处理一些不是id选择器,也不是标签,不是html代码的情况,这样直接调用jQuery.makeArray函数进行处理
        return jQuery.makeArray( selector, this );
    };

至于这个jQuery.makeArray函数,大概就是下篇文章的内容了,接下来要上班并且过节,容许我拖一拖,●▽●。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值