一個(gè)優(yōu)秀的項(xiàng)目經(jīng)理與JQuery的故事

本文是對(duì)jQuery的起源的初步探索。先通過(guò)兩個(gè)函數(shù)來(lái)擴(kuò)展原生DOM的操作,然后引入命名空間以及對(duì)其重構(gòu),接著將該命名空間擴(kuò)大到Node上,改造一個(gè)自己的Node2,引出jQuery。

引子

  • 首先,我有一個(gè)需求===========> 要獲得一個(gè)<li>標(biāo)簽的所有兄弟元素。
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
</head>
<body>
  <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>
    <li id="item6">選項(xiàng)6</li>
  </ul>
</body>
</html>

此時(shí)你剛學(xué)完原生DOM操作,知道有nextSibling previousSibling parentNode。你發(fā)現(xiàn)貌似沒(méi)有直接一下子獲得全部兄弟元素的API啊,身為一個(gè)優(yōu)秀的90后,你果斷手寫(xiě)一個(gè)函數(shù)實(shí)現(xiàn)這個(gè)需求啊。

function getSiblings(node){
  var allChild = item2.parentNode.children 
  var childObj = {length: 0}
  for (let i = 0; i < allChild.length; i++){
    if (allChild[i] !== node){
       childObj[childObj.length] = allChild[i]
       childObj.length += 1
    }
  }
  return childObj
}

好了,以上的函數(shù)就能滿足需求了,它接受你傳入的某個(gè)元素,返回包含這個(gè)元素所有兄弟元素的偽數(shù)組。

注意: 要用item2.parentNode.children 這樣子才不會(huì)獲得文本節(jié)點(diǎn)。所以你想獲得item2的所有兄弟,只需要getSiblings(item2)

獲得所有兄弟的演示地址============>demo

  • 你剛解決了一個(gè)問(wèn)題,領(lǐng)導(dǎo)又給你提了一個(gè)需求,讓你給<li>添加一個(gè)類(lèi)

領(lǐng)導(dǎo)還沒(méi)說(shuō)完,你立馬想到了,直接item2.classList.add('類(lèi)名')啊,哈哈,我好聰明啊,不愧是優(yōu)秀的90后。

給你任意一個(gè)元素要直接加上這個(gè)類(lèi)名,別給我的一個(gè)一個(gè)的加,太二了,如果元素原來(lái)有一個(gè)不應(yīng)該存在的類(lèi)名,給我刪了,領(lǐng)導(dǎo)接著說(shuō)完全部的需求。

這...看來(lái)不能item1.classList.add('類(lèi)名') item2.classList.add('類(lèi)名') item3.classList.add('類(lèi)名')這么弱智的干了啊,那我還用函數(shù)嘛,你靈機(jī)一動(dòng)。

嗯,不愧是善于思考的90后

function addClass(node, classes){
  for (var key in classes){
    var value = classes[key]
    if (value){
      node.classList.add(key)
    } else{
      node.classList.remove(key)
    }
  }
}
沒(méi)有使用方法的時(shí)候

上圖是為添加元素的時(shí)候的item2的模樣,記住它,待會(huì)和下圖對(duì)比。

執(zhí)行方法后

可以看到,執(zhí)行方法后,item2的類(lèi)名變?yōu)閎、c,這是因?yàn)槟闶?code>addClass(item2, {a: 0, b: 1, c: true})這么調(diào)用的,意思是類(lèi)名不應(yīng)該有a,刪除a,并加上b c


以上對(duì)象的遍歷并取值用到了falsey

復(fù)習(xí)一下,js的6個(gè)falsey

  • 0
  • NaN
  • ''
  • null
  • undefined
  • false

除此之外,其他的全是true。


不過(guò)你想的太美了,領(lǐng)導(dǎo)看到你的代碼中的這個(gè)片段,直接抓狂了……

if (value){
      node.classList.add(key)
    } else{
      node.classList.remove(key)
    }
 }

這段代碼給我優(yōu)化了,明明就是一句話的事。

你回去想了一會(huì),可以這么優(yōu)化

var methodName = value ? 'add' : 'remove'
node.classList[methodName](key)

最后你把如下代碼提交。

function addClass(node, classes){
  for (var key in classes){
    var value = classes[key]
    var methodName = value ? 'add' : 'remove'
    node.classList[methodName](key)
  }
}
  • 注意一點(diǎn)上述代碼不能用點(diǎn)運(yùn)算符,要用[]運(yùn)算符,classList['add'] === classList.add

給任一元素添加類(lèi)名==========================>demo

命名空間

你完成了上面的兩個(gè)需求后,領(lǐng)導(dǎo)本著鍛煉你的原則,又給你提了新的需求。

  • 少林那,你看你這兩函數(shù)寫(xiě)的挺好的,如果分開(kāi)放,每次還要在找,不如放到一起把。
  • 我擦嘞,讓我放到一起。好吧……
  • 回來(lái)后,少林冥思苦想,突然想起了昨晚剛?cè)サ脑聣鹘值奶旄械乃拓涇?chē)
  • 我把那倆函數(shù)當(dāng)成兩種商品,我開(kāi)個(gè)超市,把它倆收起來(lái)唄。
var shaolinDom = {} //少林開(kāi)的超市
shaolinDom.addClass = addClass //把a(bǔ)ddClass這個(gè)商品收進(jìn)來(lái)
shaolin.getSibling = getSiblings //把getSiblings這個(gè)商品收進(jìn)來(lái)

那我咋用呢,該咋用就咋用唄。

shaolinDom.addClass(item5, {a: true, b: false, c: 0}) //把item5上原本的b c類(lèi)名刪掉,加上 a類(lèi)名
shaolinDom.getSiblings(item6) //獲得item6的所有兄弟元素
命名空間
  • 看著上面的代碼,我就在想啊,還是自己由優(yōu)化一下吧,免得回來(lái)改。
var shaolinDom = {}
shaolinDom.addClass = function(node, classes){
  for (var key in classes){
    var value = classes[key]
    var methodName = value ? 'add':'remove'
    node.classList[methodName](key)
  }
  
}

shaolinDom.getSiblings = function (node){
  var allChild = node.parentNode.children
  var childObj = {length: 0}
  for (let i = 0; i< allChild.length; i++){
    if (allChild[i] !== node){
      childObj[childObj.length] = allChild[i]
      childObj.length += 1
    }
  }
  return childObj
}

shaolinDom.addClass(item5, {a: true, b: false, c: 0})
var allSiblings = shaolinDom.getSiblings(item6)
console.log(allSiblings)

引入命名空間=======================>demo

命名空間的優(yōu)化=====================>demo

  • 你提交了新的需求后,領(lǐng)導(dǎo)對(duì)你刮目相看啊,今年的新員工還是不錯(cuò)的
  • 你剛想松口氣,領(lǐng)導(dǎo)接著說(shuō),少林那,你看你這兩函數(shù)啊只能在shaolinDom用啊,而且我每次要把item5傳到函數(shù)里面,每次好麻煩的啦,你改進(jìn)一下,讓我的元素可以直接調(diào)用方法唄,比如item5.getSiblings()這樣多好。這樣子操作的話,item5擁有自主權(quán),就像你買(mǎi)東西,你想去買(mǎi)那個(gè)東西你就去買(mǎi)那個(gè)東西嘛,而不是東西去選擇你啊。
  • 我擦擦嘞,想想經(jīng)理分析還是很有道理的,果然還是要繼續(xù)優(yōu)化啊……
  • 回來(lái)會(huì),我在工位上想,你不是要操作DOM嗎,還想這么item5.getSiblings()操作,那我這次直接給你干到Node的原型上
Node.prototype.addClass = function(classes){
  for (var key in classes){
    var value = classes[key]
    var methodName = value ? 'add':'remove'
    this.classList[methodName](key)
  }
  
}

Node.prototype.getSiblings = function (){
  var allChild = this.parentNode.children
  var childObj = {length: 0}
  for (let i = 0; i< allChild.length; i++){
    if (allChild[i] !== this){
      childObj[childObj.length] = allChild[i]
      childObj.length += 1
    }
  }
  return childObj
}
  • 丫的,我寫(xiě)出這些代碼之后,我瞬間感覺(jué)自己兩米了呢,趕緊去找領(lǐng)導(dǎo)
  • 領(lǐng)導(dǎo)一看,吸了一口冷氣,心想,挺牛 啊,不過(guò)我要問(wèn)問(wèn)我這小子 this的知識(shí)
  • 少林那,你這函數(shù)里面this是啥,再給我講講咋用唄
  • 我屮艸芔茻,輪到我牛了吧,我把袖子已擼,是這么回事,巴拉巴拉
  • 當(dāng)然,我要先給經(jīng)理講一下怎么用
item5.addClass({a: true, b: false, c: 0}) //既然Node原型都有了這兩函數(shù),item5是node類(lèi)型,直接用唄
console.log(item6.getSiblings())

  • 我剛寫(xiě)完,領(lǐng)導(dǎo)就叫,這addClass函數(shù)里面這就一個(gè)參數(shù)啊,getSiblings函數(shù)怎么沒(méi)參數(shù)啊
  • 哦,這個(gè)啊,我講了this后就明白了。不過(guò)講這個(gè)this之前先要講一講這個(gè)call(),方便理解
//上面的代碼等同于以下代碼
item5.addClass.call(item5, {a: true, b: false, c: 0}) //call()方法的第一個(gè)參數(shù)就是this
console.log(item6.getSiblings.call(item6))
  • 如果你把call()省了,直接用()去調(diào)用函數(shù),自己腦補(bǔ)call()就好啦,自然也就知道this是誰(shuí)啦。
  • 這小子還可以啊,不錯(cuò)。不過(guò)要繼續(xù)引導(dǎo)一下啊

進(jìn)一步升級(jí),綁定Node的原型鏈上==================>demo
call()方便理解this================================>demo

自己寫(xiě)一個(gè)構(gòu)造函數(shù)

沒(méi)多久,領(lǐng)導(dǎo)的考研又來(lái)了

  • 少林那,你看你上次吧你自己寫(xiě)的函數(shù)綁到Node上了,看似挺好,但是其他人不一定用你的這兩函數(shù)啊,你綁到Node上,多占地啊。你自己寫(xiě)一個(gè)全局函數(shù)實(shí)現(xiàn)一下相同的需求吧。
  • 呦,這次經(jīng)理說(shuō)的很對(duì)啊,我的錯(cuò),我改進(jìn)一下
  • 突然聯(lián)想到以前的各種構(gòu)造函數(shù),String() Number() Array()可以直接返回一個(gè)對(duì)象,我也這么干吧
window.Node2 = function(node){
  return {
    getSiblings: function(){
      var allChild = node.parentNode.children
      var childObj = {length: 0}
      for (let i = 0; i< allChild.length; i++){
        if (allChild[i] !== node){
          childObj[childObj.length] = allChild[i]
          childObj.length += 1
        }
      }
      return childObj
    },
    
    addClass: function(classes){
      for (var key in classes){
        var value = classes[key]
        var methodName = value ? 'add':'remove'
        node.classList[methodName](key) //閉包的使用
      }
    }
  }
}
  • 實(shí)現(xiàn)了一個(gè)全局構(gòu)造函數(shù),返回一個(gè)對(duì)象,該對(duì)象里面有兩個(gè)key,value分別又是兩個(gè)函數(shù)(又體現(xiàn)了函數(shù)是第一公墓的地位),而且還用到了閉包。
var node2 = Node2(item3) //node2就是用Node2()構(gòu)造函數(shù)構(gòu)造的返回的對(duì)象
node2.getSiblings() //對(duì)象的點(diǎn)運(yùn)算符去去操作屬性啊
node2.addClass({'a': 0, 'b': true, 'c': true})
  • 領(lǐng)導(dǎo)一看,嗯,是時(shí)候讓他見(jiàn)識(shí)真正的jQuery

自己實(shí)現(xiàn)一個(gè)構(gòu)造函數(shù)去理解=======================>demo

jQuery的雛形

  • 這次領(lǐng)導(dǎo)沒(méi)再提需求,而是自己改起了代碼
window.jQuery = function(node){
  return {
    getSiblings: function(){
      var allChild = node.parentNode.children
      var childObj = {length: 0}
      for (let i = 0; i< allChild.length; i++){
        if (allChild[i] !== node){
          childObj[childObj.length] = allChild[i]
          childObj.length += 1
        }
      }
      return childObj
    },
    
    addClass: function(classes){
      for (var key in classes){
        var value = classes[key]
        var methodName = value ? 'add':'remove'
        node.classList[methodName](key) //閉包的使用
      }
    }
  }
}
  • 你看看我改了一個(gè)位置,你看著這像啥
  • 我看了一會(huì)這難道是傳說(shuō)中的jQuery?
  • 這是它的雛形,大概意思你已經(jīng)一步一步寫(xiě)出來(lái)了,jQuey就是一個(gè)構(gòu)造函數(shù),它返回一個(gè)對(duì)象,這個(gè)對(duì)象有很多key,對(duì)應(yīng)的value又是一些函數(shù)。
  • 那怎么還用$這個(gè)操作呢
  • 哈哈,一個(gè)語(yǔ)法糖嗎,你看
window.$ = jQuery
  • 給你出個(gè)題吧,你用現(xiàn)在的知識(shí)用jQuery實(shí)現(xiàn)把某個(gè)元素變紅,最好驗(yàn)證一下,你的參數(shù)是node還是一個(gè)選擇器,提示一下,可以用querySelector(),querySelector會(huì)返回文檔中匹配指定的選擇器組的第一個(gè)元素
  • 我想了一會(huì),寫(xiě)出如下代碼
window.JQuery = function(nodeOrSelector){
  let node
  //判斷一下nodeOrSelector是node還是一個(gè)選擇器
  if(typeof nodeOrSelector === 'string'){
    node = document.querySelector(nodeOrSelector)
  } else{
    node = nodeOrSelector
  }

  return {  
    getSiblings: function(){
      var allChild = node.parentNode.children
      var childObj = {length: 0}
      for (let i = 0; i< allChild.length; i++){
        if (allChild[i] !== node){
          childObj[childObj.length] = allChild[i]
          childObj.length += 1
        }
      }
      return childObj
    },
    
    addClass: function(classes){
      for (var key in classes){
        var value = classes[key]
        var methodName = value ? 'add':'remove'
        node.classList[methodName](key)
      }
    }
  }
}

所以

//var node2 = JQuery('#item3')與下列代碼作用相同,把item3變紅
var node2 = JQuery('ul > li:nth-child(3)')
變紅啦

jQuery的雛形======================>demo

使用一下自己的jQuery

  • 少林那,你也會(huì)用初級(jí)的jQuery了,咱們練習(xí)一下,你把修改一下所有的<li>的內(nèi)容吧,可以使用querySelectorAll() 返回一個(gè)NodeList的偽數(shù)組
  • 我聽(tīng)完之后,思考了一會(huì),寫(xiě)下了如下代碼
window.JQuery = function(nodeOrSelector){
  let nodes = {}
  if(typeof nodeOrSelector === 'string'){
    let temp = document.querySelectorAll(nodeOrSelector) //NodeList
    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)
      }
    })
    
  }
   
  //等同于get、set方法
  nodes.text = function(text){
    if(text === undefined){
      var texts = []
      for (let i = 0; i < nodes.length; i++){
        texts.push(nodes[i].textContent)
      }
      return texts
    } else {
      for (let i = 0; i < nodes.length; i++){
        nodes[i].textContent = text
      }
      
    }
    
  }
  return nodes
}

控制多個(gè)<li>的內(nèi)容================================>demo

最終,少林在經(jīng)理的循循善誘下,開(kāi)始探索jQuery的道路。雖然使用量在下降,但是依然有60%的web開(kāi)發(fā)人員在用。

以上并不是完全真實(shí)的jQuery的推導(dǎo),只是大約是那個(gè)意思,可以幫助我更好的理解而已。真正的JQuery必須去看文檔,英文文檔中文文檔

jQuery我來(lái)啦~

?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • jQuery是一套跨瀏覽器的JavaScript庫(kù),簡(jiǎn)化HTML與JavaScript之間的操作。由約翰·雷西格(...
    靜候那一米陽(yáng)光閱讀 915評(píng)論 0 18
  • 1.JQuery 基礎(chǔ) 改變web開(kāi)發(fā)人員創(chuàng)造搞交互性界面的方式。設(shè)計(jì)者無(wú)需花費(fèi)時(shí)間糾纏JS復(fù)雜的高級(jí)特性。 1....
    LaBaby_閱讀 1,262評(píng)論 0 1
  • 1.JQuery 基礎(chǔ) 改變web開(kāi)發(fā)人員創(chuàng)造搞交互性界面的方式。設(shè)計(jì)者無(wú)需花費(fèi)時(shí)間糾纏JS復(fù)雜的高級(jí)特性。 1....
    LaBaby_閱讀 1,493評(píng)論 0 2
  • 警告請(qǐng)使用 document.write() 僅僅向文檔輸出寫(xiě)內(nèi)容。如果在文檔已完成加載后執(zhí)行 document....
    鹿守心畔光閱讀 2,893評(píng)論 3 104
  • Canvas的drawText繪制文本自動(dòng)換行(支持設(shè)置顯示最大行數(shù)) 使用Canvas的drawText繪制文本...
    三也視界閱讀 9,701評(píng)論 0 12

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