JavaScript入門筆記[5]:jQuery的山寨源碼

jQuery的靈感來源,必定是俄羅斯套娃吧

jQuery是目前最爲長壽的JS函數庫,還有日本up主小哥專門拍了一個視頻。雖然從網上看到的,都是唱衰jQuery的聲音,可是做爲一隻菜雞,我仍是從jQuery開始個人框架學習吧。api


在我這隻菜雞看來,jQuery令我感到恐懼的地方,是它的鏈式操做,不就是套娃麼數組

鏈式操做的核心在於,jQuery返回的是一個jQuery對象,一個擁有jQuery所有功能的對象。框架


按照上述的說法,我大膽猜想,jQuery的本質是一個構造函數,雖然它不須要使用New。那麼,就試試看復現jQuery的源碼吧~ide

window.jQuery = function(selector){
//要返回一個可操做的對象,因此jQuery是一個全局函數
    const elements = document.querySelectorAll(selector); 
    
    //咱們要建立一個jQuery返回的jQuery對象api
    const api ={
    //拿addClass功能做爲例子,要注意addClass是api對象的功能
    addclass(className){
        for(let i=0; i<elements.length; i++){
            elements[i].classList.add(className);
        }
     //return api; 運行結束以後返回一個能夠調用操做的對象
     //由於是返回調用該函數的對象,因此能夠使用this
     return this;
      }
    }
    return api;
    //由於調用jQuery函數的對象是window
    //因此此處return this; 返回的是window對象
}
window.$ = window.jQuery;
//這樣,咱們的jQuery函數就能夠拿$符直接操做了
複製代碼

既然咱們找到了鏈式操做的返回對象,那麼咱們能夠更近一步,在調用函數以後直接返回一個對象,咱們的jQuery雖然不是一個構造函數,可是經過return,實現了構造對象的功能函數

window.jQuery = function(selector){
//要返回一個可操做的對象,因此jQuery是一個全局函數
    const elements = document.querySelectorAll(selector); 
    
    //咱們能夠直接返回一個對象
    return{
    addclass(className){
        for(let i=0; i<elements.length; i++){
            elements[i].classList.add(className);
        }後返回一個能夠調用操做的對象
     //由於是返回調用該函數的對象,因此使用this
     return this;
      },
      
    //咱們新增一個find功能
    find(selector,scope){
        const arr = [];
        for(let i=0;i<elements.length;i++){
        //若是scope範圍節點存在,則能夠在範圍節點的範圍內搜索元素
        arr = arr.concat(Arry.from(
        (scope || document).querySelectorAll(selector))); 
        }
    //問題來了,咱們的find若是返回查找結果的話,豈不是喪失了鏈式操做
    return arr;
    }
    }
    //調用jQuery函數的對象是window
    //若是此處return this; 返回的是window對象
}
window.$ = window.jQuery;
複製代碼

在咱們簡化告終構的jQuery函數中,添加了新的功能find學習

find(selector,scope){
        const arr = [];
        for(let i=0;i<elements.length;i++){
        //若是scope範圍節點存在,則能夠在範圍節點的範圍內搜索元素
        arr = arr.concat(Array.from(
        (scope || document).querySelectorAll(selector))); 
        }
    //問題來了,咱們的find若是返回查找結果的話,豈不是喪失了鏈式操做
    return arr;
    }
複製代碼

通過find查找後,咱們會得到一個新的數組arr,arr包含了咱們所須要的元素,可是直接返回arr,意味着鏈式操做會終止ui

咱們不能繼續return this;麼?this

若是繼續return this;,咱們返回的是調用find的對象spa

咱們要怎麼繼續鏈式操做呢?prototype

讓arr成爲咱們return的jQuery對象。

要怎麼讓讓arr成爲咱們return的jQuery對象?

咱們能夠套娃,讓jQuery再調用一次arr,讓arr包裝成jQuery對象

✿✿ヽ(°▽°)ノ✿

可是咱們的jQuery只能接收字符串不是麼

find(selector,scope){
        const arr = [];
        for(let i=0;i<elements.length;i++){
        //若是scope範圍節點存在,則能夠在範圍節點的範圍內搜索元素
        arr = arr.concat(Array.from(
        (scope || document).querySelectorAll(selector))); 
        }
    //問題來了,咱們的find若是返回查找結果的話,豈不是喪失了鏈式操做
    return jQuery(arr);
    }
複製代碼

可是咱們的jQuery只能接收字符串不是麼

咱們能夠修改jQuery函數吖

window.jQuery = function(selectorOrArry){
    let elements; 
    //const只支持在聲明時爲變量賦值,因此這裏採用let
    
    
    if(selectorOrArry === "string"){
    //咱們對輸入進行判斷,若是是字符串就當作選擇器
    elements= document.querySelectorAll(selectorOrArry); 
   }else if(selectorOrArry instanceof Array) {
   //若是輸入是數組,就進行套娃
    elements= selectorOrArry;
   }
   
    return{
    //這裏就是咱們所返回的jQuery元素
    }
}
window.$ = window.jQuery;
複製代碼

至此,咱們實現了jQuery的基本鏈式套娃操做。

強大的jQuery鏈式套娃操做就這麼簡單嗎?不不不,咱們的套娃怎會如此善罷甘休。jQuery真正厲害的地方,在於提供了end(),能夠返回上一次操做的jQuery對象。

既然end()太騷了這麼厲害,咱們也要努力山寨一下。

window.jQuery = function(selectorOrArry){
    let elements; 
    //const只支持在聲明時爲變量賦值,因此這裏採用let
    
    
    if(selectorOrArry === "string"){
    //咱們對輸入進行判斷,若是是字符串就當作選擇器
    elements= document.querySelectorAll(selectorOrArry); 
   }else if(selectorOrArry instanceof Array) {
   //若是輸入是數組,就進行套娃
    elements= selectorOrArry;
   }
   
    return{
    oldApi: selectorOrArry.oldApi,
    //咱們給返回的jQuery對象新增oldApi屬性
    end(){
    //end只須要返回oldApi
        return this.oldApi;
    },
    
    find(selector,scope){
        const arr = [];
        for(let i=0;i<elements.length;i++){
        //若是scope範圍節點存在,則能夠在範圍節點的範圍內搜索元素
        arr = arr.concat(
        Array.from((scope || document).querySelectorAll(selector))); 
        }
    arr.oldApi = this;
    //咱們將須要存檔的jQuery對象做爲屬性增長到arr對象上
    //雖然arr是數組,數組也是對象,也有屬性
    return jQuery(arr);
    //這樣,咱們在套娃的時候,就會將oldApi一併返回
    }
    }
}
window.$ = window.jQuery;
複製代碼

截止目前,咱們基本山寨完成了jQuery的套娃操做。可是若是每次操做都新增一個jQuery對象,也就意味着每一個對象都會重複建立一樣的對象屬性,佔據額外的空間。

若是引入對象的原型鏈,咱們的jQuery就更像一個構造函數了就能夠更高效了。

window.jQuery = function(selectorOrArry){
    let elements; 
    if(selectorOrArry === "string"){
    elements= document.querySelectorAll(selectorOrArry); 
   }else if(selectorOrArry instanceof Array) {
    elements= selectorOrArry;
   }
   //咱們新建一個返回的jQuery對象:api,該對象能夠直接調用 jQuery.prototype
   const api = Object.creat(jQuery.prototype);
   //api須要有elements 和 oldApi屬性
   api.elements = elements;
   api.oldApi = selectorOrArry.oldApi;
   
   return api;
   }
   
   window.$ = window.jQuery;
   
   //設置jQuery的原型對象
    jQuery.prototype = {
    constructor: jQuery,
    
    end(){return this.oldApi;},

    find(selector,scope){
        const arr = [];
        for(let i=0;i<this.elements.length;i++){
        //find函數不能直接調用jQuery函數內的對象,只能調用調用find屬性的對象的屬性
        arr = arr.concat(
        Array.from((scope || document).querySelectorAll(selector)));}
    arr.oldApi = this;
    return jQuery(arr); }
    }

複製代碼

至此,咱們實現了jQuery的鏈式操做山寨源碼。讓咱們來欣賞一下jQuery源碼又是怎麼處理的:

(function(root){
    var jQuery = function{
    //返回對象,jQuery實現對象的建構以及原型的繼承
        return new jQuery.prototype.init();
    }
    jQuery.prototype{
       init(){ }
    }
    
    //原型共享
    jQuery.prototype.init.prototype = jQuery.prototype;
    
    root.$ = root.jQuery = jQuery;
})(this)
複製代碼
相關文章
相關標籤/搜索