【JS編程接口】手寫DOM庫

1、我如今對封裝的理解

  • DOM的API太很差用了,每一個API又臭又長。
  • 咱們寫一個新對象dom或者函數JQuery,裏面定義一些簡單的函數(這些函數是用DOM的函數寫出來的,也就是DOM的函數的簡單化)。這樣咱們直接用這些簡單的函數就好了。
  • 把DOM操做封裝就是:把DOM複雜的API全包起來,成爲一個庫(DOM庫),而後創造出些接口用來和外界交流。這些接口就是咱們新定義的簡單函數。
  • 接口就是簡單函數,封裝就是DOM函數被簡單函數所用且被咱們所拋棄。

2、一些術語

  • 咱們把提供給其餘人用的工具代碼叫作庫
  • 好比jQuery、Underscore
  1. API
  • 庫暴露出來的函數或屬性叫作API (應用編程接口)
  1. 框架
  • 當你的庫變得很大,而且須要學習才能看懂,
  • 那麼這個庫就叫框架,好比Vue / React
  1. 注意
  • 編程界的術語大部分都很隨便,沒有固定的解釋
  • 因此意會便可

3、兩種風格封裝DOM操做

(一)對象風格(命名空間風格)

(二)jQuery風格(鏈式風格)

4、對象風格封裝DOM操做

所有代碼看個人github的DOM-1項目html

(一)準備工做

1. 新建文件夾DOM-一、文件index.html、文件main.js、文件dom.js

2. 初始化index.html

  • 語言改爲zh
  • title改爲「手寫jQuery」
  • 寫個‘你好’
  • 先引用dom.js<script src="dom.js"></script>
  • 再引用main.js<script src='main.js'></script>,由於main.js須要使用dom.js的代碼

3. 初始化dom.js

創造dom是個全局對象,在裏面寫函數,誰均可以用git

window.dom = {
add(){console.log('我是dom')}
}
複製代碼

4. 初始化main.js

  • main.js裏面就可使用dom
  • dom.add()

五、用parcel預覽效果

  • 新建終端
  • parcel src/index.html
  • 打開網頁,看見‘你好’
  • 打開控制檯看見'我是dom'
  • 成功

5、jQuery風格封裝DOM操做

所有代碼看個人github的DOM-2項目github

(一)準備工做

1. 新建文件夾DOM-一、文件index.html、文件main.js、文件jQuery.js

2. 初始化index.html

  • 語言改爲zh
  • title改爲「手寫jQuery」
  • 寫個‘你好’
  • 先引用jQuery.js<script src="jQuery.js"></script>
  • 再引用main.js<script src='main.js'></script>,由於main.js須要使用jQuery.js的代碼

3. 初始化jQuery.js

創造jQuery是個全局函數編程

window.jQuery = function () {  
    console.log('我是jQuery')
}
複製代碼

4. 初始化main.js

  • main.js裏面就可使用jQuery
  • jQuery()

五、用parcel預覽效果

  • 新建終端
  • parcel src/index.html
  • 打開網頁,看見‘你好’
  • 打開控制檯看見'我是jQuery'
  • 成功

(二)重點開始

一、寫jQuery函數

(1)初版代碼:(閉包)傻瓜代碼,爲了讓你知道最詳細的過程

window.jQuery = function(selector) {
  //1.jQuery是個全局函數,咱們要給他一個選擇器當參數
  const elements = document.querySelectorAll(selector); //2.他會選出知足條件的節點,組成一個僞數組叫element
  const api = {
    //3.他會生成一個叫api的對象,這個對象裏的每個函數都和elements造成閉包,也就是每一個函數都要用到elements
    //用這個api對象裏的任意一個函數均可以操做elements
    addClass(className) {
      //給我一個class的名字,我會遍歷每一個element中的元素,給他們都加上className
      for (let i = 0; i < elements.length; i++) {
        elements[i].classList.add(className);
      }
    }
  };
  return api; //4.最後返回對象api,這個對象裏的函數能夠對選中的元素(elements)操做
};
//你想選出(操做)哪些元素,你就把對應的選擇器給jQuery函數,而後他會給你能夠操做這些元素的函數集合(api對象)
//我想對.test的元素操做,那麼jQuery(.test)就是一個叫api的對象,他包含了全部能夠對.test元素操做的函數
//jQuery(.test).addClass('yy') 這樣就對全部.test元素加上個叫yy的class
複製代碼

(2)第二版代碼:(鏈式操做)思考return啥

window.jQuery = function(selector) {
  const elements = document.querySelectorAll(selector); 
  const api = {
    addClass(className) {
      for (let i = 0; i < elements.length; i++) {
        elements[i].classList.add(className);
      }
      return api  //執行完函數後仍是返回api,能夠繼續對elements操做
    }
  };
  return api; 
};
//jQuery(.test).addClass('yy') 這樣就對全部.test元素加上個叫yy的class,可是返回值還是api,也就是說咱們能夠繼續對.test操做
//jQuery(.test).addClass('yy').addClass('bb')對全部.test元素加上個叫yy的class,由於返回值是api,咱們仍可對全部.test元素操做加上bb的class

複製代碼

注意到,jQuery(.test).addClass('yy')的this就是jQuery(.test)也就是api。 jQuery(selector).addClass('yy')的this===jQuery(selector)===api,因此改爲return thisapi

window.jQuery = function(selector) {
  const elements = document.querySelectorAll(selector); 
  const api = {
    addClass(className) {
      for (let i = 0; i < elements.length; i++) {
        elements[i].classList.add(className);
      }
      return this  //執行完函數jQuery(selector).addClass(xxx)後仍是返回this(=jQuery(selector)),還能夠繼續對elements操做
    }
  };
  return api; 
};
複製代碼

window.jQuery(selector)的this是window,不要暈倒了數組

(3)第三版代碼:優化,把api弄掉節省內存,由於咱們建立了api對象,又返回api對象,直接返回一個對象不就好嗎(知道爲啥前面要把return從api改爲this了吧)

window.jQuery = function(selector) {
  const elements = document.querySelectorAll(selector);

  return {
    addClass(className) {
      for (let i = 0; i < elements.length; i++) {
        elements[i].classList.add(className);
      }
      return this;
    }
  };
};
複製代碼

(4)小總結

jQuery(選擇器)用於獲取對應的元素bash

  • 但它卻不返回這些元素
  • 相反,它返回一個對象,稱爲jQuery構造出來的對象(簡稱jQuery對象)
  • 這個對象能夠操做對應的元素
jQuery(.test) 返回 當前的JQuery對象
jQuery(.test).addClass('yy') 操做:給全部.test元素加上classyy 返回:this===jQuery(.test) ----當前的JQuery對象
jQuery(.test).addClass('yy').addClass('bb') 操做:給全部.test元素加上classbb 返回:this===jQuery(.test).addClass('yy') ----當前的JQuery對象
複製代碼

jQuery是一個不須要加new的構造函數。閉包

  • jQuery()能構造一個對象,並且不用加new
  • jQuery不是常規意義上的構造函數,這是由於jQuery用了一些技巧(目前不必知道)

window.$ = window.jQuery之後用$()就行框架

④用$開頭的名字給JQuery對象命名!const $div=jQuery(.test)易於和普通DOM標籤區分!dom

二、寫find()函數以及發現鏈式操做的問題

(1)初版代碼:寫find函數

window.jQuery = function(selector) {
  const elements = document.querySelectorAll(selector);

  return {
    //對elements操做
    //1、查
    //find函數用於找某些元素
    find(selector) {
      let array = []; //這個數組將儲存結果元素
      for (let i = 0; i < elements.length; i++) {
        //對elements中每個元素中來找其中的結果元素
        array = array.concat(
          Array.from(elements[i].querySelectorAll(selector))
        );
        //全部結果元素會成爲僞數組,把僞數組變成真數組,在鏈接到array中去,成爲新的array
      }
      return array;
    },


  };
};
複製代碼

那完了,鏈式結構中斷了。jQuery('.test').find('.children')函數返回的是數組array,不是this了!若是返回this,那就是那個jQuery對象了,以後仍是對.test對象操做,那你找.children幹啥??

(2)第二版代碼:修改jQuery函數

  • 咱們發現jQuery函數不能只接受選擇器,必需要接受個數組成爲elements
  • 第一步:重載:當參數爲字符串時爲選擇器,elements是選出的那些元素組成的僞數組;當參數爲數組時,elements就是數組自己
  • 第二步:須要數組成爲新的jQuery對象時,就要從新用jQuery(數組)成爲新的jQuery對象,而後return他;不然直接return this返回當前的JQuery對象就行
window.jQuery = function (selectorOrArray) {
  let elements //const聲明時必須賦值並且還不能改,因此只能用let了//不能夠在下面elements前聲明,否則就只在那個塊級做用域裏有用了
  if (typeof selectorOrArray === 'string') {  //若是是字符串,那就是選擇器,選出對應元素
    elements = document.querySelectorAll(selectorOrArray)
  } else if (selectorOrArray instanceof Array) { //若是是數組,那就是它自己
    elements =selectorOrArray
  }

  return {
    //對elements操做
    find(selector) {
      let array = []; 
      for (let i = 0; i < elements.length; i++) {
        array = array.concat(
          Array.from(elements[i].querySelectorAll(selector))
        );
      }
      const newApi = jQuery(array) //咱們不能夠return以前的api了,否則你find出這些元素幹啥?確定要對find出的元素搞事情了啊。//因此發現jQuery不能只接受選擇器,還要有數組
                                   //生成全新的jQuery對象了(專屬這個數組裏元素的函數集合)
      return newApi;
      //前兩句請合併爲 return jQuery(array)
    },

    addClass(className) {
      for (let i = 0; i < elements.length; i++) {
        elements[i].classList.add(className);
      }
      return this;
    }
  };
};
複製代碼

三、end函數:鏈式問題還沒解決:我還想回到以前的舊的jQuery對象咋辦?

  • 在新建數組的JQuery對象jQuery(數組)前,要先把當前的JQuery對象加進數組裏,成爲一個新屬性oldApi
  • 給JQuery對象加一個新屬性oldApi:selectorOrArray.oldApi
  • end函數:返回當前jQuery對象的oldApi
window.jQuery = function(selectorOrArray) {
  let elements;
  if (typeof selectorOrArray === "string") {
    elements = document.querySelectorAll(selectorOrArray);
  } else if (selectorOrArray instanceof Array) {
    elements = selectorOrArray;
  }

  return {
    oldApi: selectorOrArray.oldApi, //給JQuery對象加一個屬性,值爲selectorOrArray的oldApi屬性

    find(selector) {
      let array = [];
      for (let i = 0; i < elements.length; i++) {
        array = array.concat(
          Array.from(elements[i].querySelectorAll(selector))
        );
      }
      array.oldApi = this; //先把當前的jQuery對象放到即將要成爲新的elements的數組中去,成爲一個oldApi屬性
      return jQuery(array); //在返回 新建的 數組的jQuery對象
    },
    end() {
      return this.oldApi;
    }, //this爲數組的JQuery對象了,他有個oldApi屬性,屬性值爲selectorOrArray(也就是數組)的屬性oldApi

    addClass(className) {
      for (let i = 0; i < elements.length; i++) {
        elements[i].classList.add(className);
      }
      return this;
    }
  };
};

複製代碼
api1=jQuery('.test')
api2=jQuery('.test').find('.children')
api2.end()//返回this的oldApi。
          //this===api2===當前的JQuery對象。
          //oldApi:selectorOrArray.oldApi=array.oldApi=(當時的this)=(當時的JQuery對象api2)
複製代碼
相關文章
相關標籤/搜索