1. 程式人生 > >jquery3.0原始碼解讀(三)Selector

jquery3.0原始碼解讀(三)Selector

前置知識

  • 正則表示式
  • css選擇器
  • DOM
  • call

程式碼概況

第一節將jQuery初始化的時候,我們只是簡單講了下jQuery的構造方式,並沒有分析jQuery物件到底是如何具體構造出來的。

使用過jquery的同學一定知道,jquery的一個最大的用的最多的特性就是用來選擇頁面上的元素,這個真是太方便,太強大了。換句話說,構造jquery物件的過程,就是選擇器的實現過程。下面,我們來分析選擇器的實現(其實是jquery物件的構造過程)。

開啟/src/selector.js,程式碼如下:

define( [ "./selector-sizzle" ], function()
{
} );

直接指向selector-sizzle,那我們接著開啟/src/selector-sizzle.js,程式碼如下:

define( [
    "./core",
    "../external/sizzle/dist/sizzle"
], function( jQuery, Sizzle ) {

"use strict";

jQuery.find = Sizzle;
jQuery.expr = Sizzle.selectors;

// Deprecated
jQuery.expr[ ":" ] = jQuery.expr.pseudos;
jQuery.uniqueSort = jQuery.unique
= Sizzle.uniqueSort; jQuery.text = Sizzle.getText; jQuery.isXMLDoc = Sizzle.isXML; jQuery.contains = Sizzle.contains; jQuery.escapeSelector = Sizzle.escape; } );

注意到jQuery.find = Sizzle;,這個Sizzle本身就是一個純javascript實現的css選擇器引擎。換句話說,就是css選擇器的功能,用js也能做到。光是用這個,就完成了jquery選擇器的大部分功能。

jQuery的find功能我們也經常使用,這裡其實就是直接使用了Sizzle引擎。

功能說明

jquery的選擇器大概有如下幾種功能用法(這裡又是大量的過載):

  1. jQuery(selector[,context])
    在context(作為待查詢的 DOM 元素集、文件或 jQuery 物件)中通過selector(用來查詢的字串)查詢所有匹配的元素

  2. jQuery(element)
    把element(DOM元素)封裝成jQuery物件

  3. jQuery(elementArray)
    把elementArray(DOM元素陣列)封裝成jQuery物件

  4. jQuery(Object)
    把Object(任意物件)封裝成jQuery物件

  5. jQuery(jQueryObject)
    相當於克隆了一個jQueryObject物件

  6. jQuery(html[,owerDocument])
    使用html字串(用於動態建立DOM元素的HTML標記字串),在owerDocument(建立DOM元素所在的文件)動態建立由jQuery物件包裝的DOM元素

  7. jQuery()
    建立空jquery物件

  8. jQuery(html,attributes)
    使用html字串(用於動態建立DOM元素的HTML標記字串)動態建立由jQuery物件包裝的DOM元素,並設定attributes(用於附加到新建立元素上的屬性、事件和方法)

  9. jQuery(callback)
    允許你繫結一個在DOM文件載入完成後執行的函式,$(document).ready()的簡寫。

原始碼分析

原始碼如下:

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

        if ( !selector ) {
            return this;
        }

        root = root || rootjQuery;

        if ( typeof selector === "string" ) {
            if ( selector[ 0 ] === "<" &&
                selector[ selector.length - 1 ] === ">" &&
                selector.length >= 3 ) {

                match = [ null, selector, null ];

            } else {
                match = rquickExpr.exec( selector );
            }

            if ( match && ( match[ 1 ] || !context ) ) {

                if ( match[ 1 ] ) {
                    context = context instanceof jQuery ? context[ 0 ] : context;

                    jQuery.merge( this, jQuery.parseHTML(
                        match[ 1 ],
                        context && context.nodeType ? context.ownerDocument || context : document,
                        true
                    ) );

                    if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) {
                        for ( match in context ) {

                            if ( jQuery.isFunction( this[ match ] ) ) {
                                this[ match ]( context[ match ] );

                            } else {
                                this.attr( match, context[ match ] );
                            }
                        }
                    }

                    return this;

                } else {
                    elem = document.getElementById( match[ 2 ] );

                    if ( elem ) {

                        this[ 0 ] = elem;
                        this.length = 1;
                    }
                    return this;
                }

            } else if ( !context || context.jquery ) {
                return ( context || root ).find( selector );

            } else {
                return this.constructor( context ).find( selector );
            }

        } else if ( selector.nodeType ) {
            this[ 0 ] = selector;
            this.length = 1;
            return this;

        } else if ( jQuery.isFunction( selector ) ) {
            return root.ready !== undefined ?
                root.ready( selector ) :

                selector( jQuery );
        }

        return jQuery.makeArray( selector, this );
    };

第一步:判斷selector是否為空,是的話,直接返回this,也就是空的jquery物件。用法7解決;

第二步:定義了root為rootjQuery,這裡的rootjQuery其實就是jQuery( document ),主要用於選擇器為空的時候,用rootjQuery上下文來代替空值,繼續下面的鏈式操作(關於鏈式操作,我們在下一節中講);

第三步:判斷selector是否為字串、是否為DOM型別、是否是一個function。字串先保留;如果是DOM型別的話,this[ 0 ] = selector;長度賦值1,用法2解決;如果是function的話,判斷document是否ready,如果ready,就執行function,用法9解決(關於ready的實現,見之前的文章);

第四步:如果selector為字串,通過正則判斷selector是不是html字串,如果不是,判斷有沒有context,有的話,使用context呼叫find方法(也就是sizzle),沒有就是用document為context呼叫find。用法1解決;

第五步:如果selector為html字串,這裡說明一下rquickExpr,這個是用來匹配HTML標記和ID表示式,匹配後的結果,也就是match,為[找到的匹配,HTML標記,ID]。判斷match[1]也就是HTML標記是否存在,如果不存在,直接通過match[ 2 ]也就是ID去取對應元素;

第六步:如果match[1]存在,jquery通過parseHTML(將html字串轉換為dom)和merge(把第二個陣列merge到第一個陣列)方法,將HTML標記轉化為由jQuery物件包裝的DOM元素,並返回,用法6解決;如果有attributes的話,新增屬性,用法8解決;

第七步:剩下的還有用法3,4,5,它們都會被jQuery.makeArray處理。大致的意思就是處理成jquery陣列,這裡的makeArray對外是將一個類陣列物件轉換為真正的陣列物件,對內有個過載,就是處理成jquery陣列物件。