jQuery源碼剖析 (二) - 選擇器

jQuery 源碼解析代碼及更多學習乾貨: 猛戳GitHubhtml

建議下載源碼而後據文章思路學習,最好本身邊思考邊多敲幾遍。前端

一:剖析源碼前準備

  • 1.首先官網下載源碼jQuery官網
  • 2.選擇jQuery版本並下載到本地,並在本地給本身新建件myjQuery-1.0.1.js(這個文件是用來仿寫jQuery).
  • 3.建立入口文件並引入這官方jQuery和本身建立的myjQuery-1.0.1.js文件.
  • 4.開始剖析源碼.

二.開始剖析

本次主要剖析jQuery選擇器的封裝,在平時使用jQuery時,很是容易的經過$()就能夠查找相關的選擇器,那麼具體是如何實現的呢?跟隨我來一探究竟...node

首先先來作個例子: 咱們先經過官方的jQuery框架輸出如下幾個實例:jquery

// 傳入字符串
console.log($("a"));    //建立DOM節點包裝成jQuery對象
// 傳入HTML
console.log($("<div>"));  // //建立DOM節點包裝成jQuery對象
// 傳入對象
console.log($(document));
// 傳入選擇器
console.log($('.box'));
// 傳入對象
console.log($(this));   // 把傳入的對象包裝成jQuery對象 
// 傳入方法 
$(function(){
    console.log(11111);  //這個是在頁面加載完成後加載執行的,等效於在DOM文檔加載完成後執行了$(document).read()方法        
})

複製代碼

輸出結果:git

根據輸入的結果咱們類閱讀源碼,反推如何實現相似的效果: 首先$()調用的是jQuery.prototype.init()的初始化方法,因此要在init()方法裏作擴展。
如下僅僅是核心的代碼片斷,詳細代碼在github上,自行下載查看.
剖析源碼下載

init:function (selector,context) {
            context = context || document;
            var match,elem,index=0;
            if(!selector){
                return this;
            }
            /**
             * 檢測傳過來的數據是不是字符串
             * **/
            if(typeof selector === "string"){
                if (selector.charAt(0) === "<" && selector.charAt(selector.length-1)=== ">" && selector.length>=3) {
                   // 此時是HTML  
                    match = [selector];
                }
                // match 有值的狀況
                if (match) {
                    /**
                     * 合併數組
                     * merge parseHTML是靜態擴展方法
                     * **/
                    jQuery.merge(this, jQuery.parseHTML(selector,context));
                  // 查詢DOM節點  
                } else {
                    elem = document.querySelectorAll(selector);
                    // 轉化爲真數組
                    var elems = Array.prototype.slice.call(elem);
                    this.length = elem.length;

                    for (;index = elem.length;index ++) {
                        this[index] = elems[index];
                    }
                    this.context = context;
                    this.selector = selector;
                }
                // HANDLE: $(DOMElement)
            } else if (selector.nodeType){ // 
                this.context = this[0] = selector;
                this.length = 1;
                return this;
              //  $(function)
              // 函數處理
            } else if (isFunction( selector )) { 
                jQuery(document).ready(selector); // 實例對象的方法
            }
            /**
            *makeArray 是jQuery擴展的方法
            **/
            return jQuery.makeArray( selector, this );
        },
複製代碼
jQuery 擴展的靜態方法
        /**
         *  合併數組
         *  [first] jQuery的實例對象  this
         *  [second] DOM 節點 
         */
        merge:function (first,second) {
            var l = second.length, // 1
                i = first.length, // 0
                j = 0;
            if (typeof l === "number") {
                for ( ; j < l; j++){ // 遍歷DOM節點
                    first[i++] = second[j];
                }
            } else {
                while (second[j] !== undefined) {
                    first[i++] = second[j++];
                }
            } 
            first.length = i;
            // 返回jQuery的實例對象
            return first;   
        },
        /**
         * 解析HTML
         * [data] 傳入的數據
         * [context] 返回的值
         * **/
        parseHTML:function (data,context) {
            if (!data || typeof data !== "string") {
                return null;
            }
            /**
             * exec() 是正則方法 返回爲數組   
             * [0] 爲正則表達式相匹配的文本
             * [1] 表達式相匹配的文本    
             * **/
            // 過濾掉符號,只提取標籤 "<a>" ==> "a"
            var parse = rejectExp.exec(data);
            // 返回一個建立DOM的元素
            return [context.createElement(parse[1])];
        },
        /**
         * 將一個類數組對象轉換爲真正的數組對象
         * [arr] 傳入的數組
         * [result] 返回的數組
         * **/
        makeArray:function(arr,result){
            var ret = result || [];
            if ( arr != null ) {
                if ( isArrayLike( Object( arr ) ) ) {
                    jQuery.merge( ret,
                        typeof arr === "string" ?
                        [ arr ] : arr
                    );
                } else {
                   [].push.call( ret, arr );
                }
            }
            return ret;
        },
        isReady:false,
        readylist:[],// list 
        ready:function(){ // 事件函數
           jQuery.isReady = true;
           jQuery.readylist.forEach(function(callback){
               callback.call(document);
           })  
           // 清空
           jQuery.readylist = null;
        }
    });
複製代碼
/**
     *  定義全局函數
     * **/
    
    // 判斷是不是方法
    var isFunction = function isFunction( obj ) {   
        return typeof obj === "function" && typeof obj.nodeType !== "number";
        };
    // 判斷是不是windows
    var isWindow = function isWindow( obj ) {
        
		return obj != null && obj === obj.window;
    };
    
複製代碼

經過以上代碼能夠作如下幾點分析:
1.$()傳過來的selector數據是屬於什麼類型?不一樣的數據類型,分析不一樣數據類型的行爲,有如下幾種狀況:
github

  • 1.1 若是傳過來的數據是字符串:那麼要分析字符串是不是HTML標籤,若是是HTML那麼就經過正則提取關鍵字並建立一個HTM標籤輸出
  • 1.2 若是傳過來的數據是否是html元素,那麼要經過querySelectorAll來查詢過濾,若是能夠查詢到是DOM中的選擇器,那麼就遍歷輸出他的值.
  • 1.3 若是傳過來的元素是DOM節點,直接返回
  • 1.4 若是傳過來的數據是一個對象方法,那麼要經過$(document).read()方法,監聽攔截DOMContentLoaded方法,改變對象方法的指針而後依次加入到數組中,輸出.

經過剖析jQuery選擇器模塊,進行測試:正則表達式

測試代碼:windows

// 建立DOM
        // 傳入字符串
        console.log($("a"));    //建立DOM節點包裝成jQuery對象
        // 傳入HTML
        console.log($("<div>"));  // //建立DOM節點包裝成jQuery對象
        // 傳入對象
        console.log($(document));
        // 傳入選擇器
        console.log($('.box'));
        // 傳入對象
        console.log($(this));   // 把傳入的對象包裝成jQuery對象
        $(function(){
            console.log(11111); 
複製代碼

輸出結果:設計模式

至此完美收官 !! 😊

總結

經過剖析jQuery源碼選擇器部分,有如下幾點我的收穫:
數組

  • 1.jQuery 設計的靜態屬性擴展和jQuery的原型鏈屬性擴展(實例擴展)的巧妙應用
  • 2.巧妙應用正則來篩選數據

其餘

jQuery 源碼剖析 系列目錄地址:猛戳GitHub

jQuery 源碼剖析 系列預計寫十篇左右,旨在加深對原生JavaScript 部分知識點的理解和深刻,重點講解 jQuery核心功能函數、選擇器、Callback 原理、延時對象原理、事件綁定、jQuery體系結構、委託設計模式、dom操做、動畫隊列等。

若是有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝。若是喜歡或者有所啓發,歡迎 star⭐️,對做者也是一種鼓勵。

關注公衆號回覆:學習 領取前端最新最全學習資料,也能夠進羣和大佬一塊兒學習交流

相關文章
相關標籤/搜索