jQuery的原理理解及仿寫

先寫段代碼

var dom = {}
dom.getSiblings(node)
dom.addClass(node, {a: true, b: false})

dom就是命名空間,一個(gè)對(duì)象,對(duì)后面的封裝和理解有幫助

 <ul>
  <li id="item1">選項(xiàng)1</li>
  <li id="item2">選項(xiàng)2</li>
  <li id="item3">選項(xiàng)3</li>
  <li id="item4">選項(xiàng)4</li>
  <li id="item5">選項(xiàng)5</li>
</ul>

var allChildren = item3.parentNode.children
var array = {length:0}
for(let i = 0;i < allChildren.length; i++){
  if(allChildren[i] !== item3){
    array[array.length] = allChildren[i];    //不是數(shù)組沒(méi)有push方法
    array.length += 1
  }
}
console.log(array)

上面的代碼實(shí)現(xiàn)的效果是獲得節(jié)點(diǎn)的兄弟姐妹
console.log輸出的是除了item3以外的其他兄弟節(jié)點(diǎn),即item1, #item2, item4, #item5

接下來(lái)把代碼進(jìn)行封裝

function getSiblings(node){
  var allChildren = node.parentNode.children
  var array = {length:0}
  for(let i = 0;i < allChildren.length; i++){
     if(allChildren[i] !==node){
       array[array.length] = allChildren[i];    
       array.length += 1
     }
  }
  return array
}
console.log(getSiblings(item3))

接下來(lái)增加實(shí)現(xiàn)刪除添加class類功能的代碼

var classes = {'a':true,'b':false,'c':true}
for( let key in classes ){
  var value = classes[key]
  if(value){
    item3.classList.add(key)
  }else{
    item3.classList.remove(key)
  }
}

我們通過(guò)審查元素item3就可以看到item3上多了abclass類,
封裝刪除添加class類的代碼

function addClass(node,classes) {
  for (let key in classes) {
    var value = classes[key]
    if (value) {
      node.classList.add(key)
    } else {
      node.classList.remove(key)
    }
  }
}
addClass(item3,{'a':true,'b':false,'c':true})

將上面兩個(gè)方法代碼合并整合,來(lái)實(shí)現(xiàn)同樣的效果

function getSiblings(node) {
  var allChildren = node.parentNode.children
  var array = {
    length: 0
  }
  for (let i = 0; i < allChildren.length; i++) {
    if (allChildren[i] !== node) {
      array[array.length] = allChildren[i]; //不是數(shù)組沒(méi)有push
      array.length += 1
    }
  }
  return array
}

function addClass(node,classes) {
  for (let key in classes) {
    var value = classes[key]
    var methodName = value ? 'add': 'remove' 
    node.classList[methodName](key)
  }
}

window.xzdom = {}
xzdom.getSiblings = getSiblings
xzdom.addClass = addClass
xzdom.getSiblings(item3)
xzdom.addClass(item3,{'a':true,'b':false,'c':true})

上面代碼的最后五行就命名空間, 是一種設(shè)計(jì)模式
命名空間非常有必要,如果沒(méi)有命名空間有兩個(gè)缺點(diǎn)1.別人不知道叫什么2.會(huì)不知不覺(jué)覆蓋全局變量。
而jQuery就是用了命名空間的模式來(lái)實(shí)現(xiàn)的。
下面正式引出了jQuery的設(shè)計(jì)原理

首先我們?cè)谠蜕霞?,Node.prototype原型上加
<ul>
  <li id="item1">選項(xiàng)1</li>
  <li id="item2">選項(xiàng)2</li>
  <li id="item3">選項(xiàng)3</li>
  <li id="item4">選項(xiàng)4</li>
  <li id="item5">選項(xiàng)5</li>
</ul>

Node.prototype.getSiblings = function(){
  var allChildren = this.parentNode.children
  var array = {
    length:0
  }
  for(let i=0; i<allChildren.length;i++){
    if (allChildren[i] !== this) {
      array[array.length] = allChildren[i]; 
      array.length += 1
    }
  }
  return array
}
Node.prototype.addClass = function(classes){
    classes.forEach((value)=>this.classList.add(value) ) 
}     //移除class的功能取消了,保留了addclass功能
console.log( item3.getSiblings() )
//  等價(jià)于  console.log(item3.getSiblings.call(item3))  //this作為第一個(gè)參數(shù)
item3.addClass( ['a','b','c'] )
// 等價(jià)于  item3.addClass.call(item3, ['a','b','c'] )   //['a','b','c']作為第二個(gè)參數(shù)

這里實(shí)現(xiàn)了在node原型上的添加class的功能,但是有個(gè)問(wèn)題,它會(huì)覆蓋全局變量的方法,比如原型上有getSiblings這個(gè)方法或者屬性,那么這里就會(huì)覆蓋掉。

其次通過(guò)修改名稱來(lái)保留原來(lái)的方法或?qū)傩?--新建一個(gè)Node2(或者叫jQuery)
window.jQuery = function(node) {   //window.Node2 = function(node) {
  return {
    getSiblings: function() {
      var allChildren = node.parentNode.children
      var array = {
        length: 0
      }
      for (let i = 0; i < allChildren.length; i++) {
        if (allChildren[i] !== node) {
          array[array.length] = allChildren[i]; //不是數(shù)組沒(méi)有push
          array.length += 1
        }
      }
      return array
    },
    addClass: function(classes) {
      classes.forEach((value) => node.classList.add(value))
    }
  }
}

var node2 = jQuery(item3)    //var node2 = Node2(item3)
console.log(node2.getSiblings())
node2.addClass(['a', 'b', 'c'])

代碼就實(shí)現(xiàn)了在item3上添加了a,b,c三個(gè)class
Node2(jQuery)接受一個(gè)舊的節(jié)點(diǎn),然后返回一個(gè)新的對(duì)象,這個(gè)對(duì)象就是Node2(jQuery)對(duì)象

下面加上選擇器
.blue{color:blue}

window.jQuery = function(nodeOrSelector) {
  let node
  if(typeof nodeOrSelector === 'string'){
    node = document.querySelector(nodeOrSelector)
  }else{
    node = nodeOrSelector
  }
  return {
    getSiblings: function() {
      var allChildren = node.parentNode.children
      var array = {
        length: 0
      }
      for (let i = 0; i < allChildren.length; i++) {
        if (allChildren[i] !== node) {
          array[array.length] = allChildren[i]; //不是數(shù)組沒(méi)有push
          array.length += 1
        }
      }
      return array
    },
    addClass: function(classes) {
      classes.forEach((value) => node.classList.add(value))
    }
  }
}
var node2 = jQuery('ul>li:nth-child(3)')     // var node2 = jQuery('#item3')
console.log(node2.getSiblings())
node2.addClass(['blue', 'b', 'c'])

代碼實(shí)現(xiàn)了在item3上添加blue,b,c三個(gè)class類,并且blue類有樣式,并且item3li變藍(lán)

同時(shí)操作6個(gè)li
.blue{color:blue;}

<ul>
 <li id="item1">選項(xiàng)1</li>
 <li id="item2">選項(xiàng)2</li>
 <li id="item3">選項(xiàng)3</li>
 <li id="item4">選項(xiàng)4</li>
 <li id="item5">選項(xiàng)5</li>
</ul>

window.jQuery = function(nodeOrSelector) {
 let nodes = {}
 if (typeof nodeOrSelector === 'string') {
   let temp = document.querySelectorAll(nodeOrSelector)
   for (let i = 0; i < temp.length; i++) {
     nodes[i] = temp[i]
   }
   nodes.length = temp.length
 } else if (nodeOrSelector instanceof Node) {
   nodes = {
     0: nodeOrSelector,
     length: 1
   }
 }
 nodes.addClass = function(classes) {
   classes.forEach((value) => {
     for (let i = 0; i < nodes.length; i++) {
       nodes[i].classList.add(value)
     }
   })
 }
 nodes.getText = function() {
   var texts = []
   for (let i = 0; i < nodes.length; i++) {
     texts.push(nodes[i].textContent)
   }
   return texts
 }
 nodes.setText = function(text) {
   for (let i = 0; i < nodes.length; i++) {
     nodes[i].textContent = text
   }
 }
 return nodes
}
var node2 = jQuery('ul > li')
node2.addClass(['blue'])
console.log(node2.getText())
node2.setText('hi')

通過(guò)全局變量的jQuerysetText方法就把6個(gè)li變藍(lán)了

getTextsetText方法放在同一個(gè)函數(shù)封裝
nodes.text = function(text) {
    if (text === undefined) {          //元素的文本內(nèi)容
      var texts = []
      for (let i = 0; i < nodes.length; i++) {
        texts.push(nodes[i].textContent)
      }
      return texts
    } else {                              //替換文本內(nèi)容,并用傳進(jìn)來(lái)的值覆蓋
      for (let i = 0; i < nodes.length; i++) {
        nodes[i].textContent = text
      }
    }
  }

這里的設(shè)置文本內(nèi)容和jQuery很像,傳一個(gè)參數(shù),如果是undefined,就獲取元素本是的文本內(nèi)容;如果有內(nèi)容就替換文本內(nèi)容,用用傳進(jìn)來(lái)的值覆蓋。

我們可以看出 jQuery是函數(shù)(有括號(hào),6種數(shù)據(jù)類型不符合,只能是對(duì)象里的函數(shù)這個(gè)類型符合)。并且是鏈?zhǔn)讲僮?,因?yàn)樯厦娴拇a在node的原型鏈上線指向了構(gòu)造出來(lái)的jQuery的原型,然后再指向Object.prototype

習(xí)俗:如果這個(gè)對(duì)象是由jQuery構(gòu)造出來(lái)的或者是$構(gòu)造出來(lái)的,就在對(duì)象前面加$,表示它是jQuery的對(duì)象

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,113評(píng)論 25 709
  • 在線閱讀 http://interview.poetries.top[http://interview.poetr...
    前端進(jìn)階之旅閱讀 115,567評(píng)論 24 450
  • 阮一峰jQuery 不要學(xué)jquery mobile 獲取某個(gè)節(jié)點(diǎn)的所有兄弟姐妹 ul>li[id=item$]{...
    tsl1127閱讀 403評(píng)論 0 2
  • 總收益 --- 企業(yè)出售產(chǎn)品所得到的錢 總成本 --- 企業(yè)用于生產(chǎn)的投入品的市場(chǎng)價(jià)值 利潤(rùn) --- 總收益 ——...
    東野雜不亂閱讀 544評(píng)論 0 0
  • 如心語(yǔ):以下是如心發(fā)起建立的第一個(gè)百人大群的群主公告?!暗谝弧?,總是讓人印象深刻、難以忘還的,所以記彔于此。 不久...
    如心1976閱讀 404評(píng)論 0 0

友情鏈接更多精彩內(nèi)容