爲何要學jQuery呢?由於在學習原生js時候,有許多DOM操做冗長複雜,而jQuery卻可使之簡單化,因此jQuery也曾被衆多程序員喜好。那麼咱們該怎麼去學習jQuery呢?所謂知己知彼百戰百勝,咱們能夠嘗試自制jQuery式的API來了解其原理,在完成這個API後咱們就能很是容易的使用jQuery。html
<!--html代碼--> <body> <h1>text</h1> <ul> <li id="item1">選項1</li> <li id="item2">選項2</li> <li id="item3">選項3</li> </ul> </body>
讓咱們來添加如下需求:java
以item2爲例,爲了獲取其全部兄弟姐妹,咱們可使用DOM API 中的ParentNode.children屬性。但這有一個問題,咱們找到節點父親後返回的是其全部孩子,爲了獲得除item2外的全部節點,咱們必須建立一個空對象,把節點父親返回的全部孩子組成的僞數組遍歷後經過條件篩選,將不等於自身節點的其餘節點放入此空對象裏。node
//javaScript代碼 function getSiblings(node) { var allChlidren = node.parentNode.chlidren 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 } //調用方法 getSiblings(item2)
以上代碼中,咱們建立的array是一個僞數組,其並無數組的push方法,那麼怎麼才能把value放進數組呢?因此使用array[array.length]=allChildren[i]的方法(不能放i,由於條件不符合時,會有i跳過了)程序員
咱們能夠建立一個classes僞數組,經過遍歷後,根據判斷其輸入的屬性值所對應的布爾值來決定使用DOM API中的node.classList.add或者remove來一次性增刪該節點的多個類名。面試
//javaScript代碼 function addClass(node,classes){ for(let key in classes){ var value = classes[key] var methodName = value? 'add' : 'remove' node.classList[methodName](key) } } // 調用addClass() addClass(item1,{ a:ture , b:false})
在知足以上兩點需求後,咱們要是想讓別人也來使用咱們這兩個函數方法,該怎麼辦呢?解決辦法就是給它添加命名空間。api
//javaScript代碼 window.mmmdom = {} mmmdom.getSiblings = function(node) { var allChlidren = node.parentNode.chlidren 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 } mmmdom.addClass = function(node,classes){ for(let key in classes){ var value = classes[key] var methodName = value? 'add' : 'remove' node.classList[methodName](key) } } // 調用方法 mmmdom.getSiblings(item3) mmmdom.addClass( item3,{a:false , b:true })
給它命名空間還有一個好處就是能夠避免不知不覺的把全局變量給覆蓋。數組
以上咱們已經知足了需求並且還可讓別人來使用,可是還有一個問題就是上面的函數調用方法都是textdom.addClass()
這樣的,可是按照咱們的習慣,咱們更喜歡使用item2.getClass('red')
這樣的調用,接下來咱們就來實現這一點吧。dom
1.擴展node接口
咱們能夠直接在 Node.prototype 上添加這個函數,這樣咱們的節點就能夠繼承到這個函數方法了。函數
Node.prototype.getSiblings =function ( ) { var allChlidren = this.parentNode.chlidren 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 ) ) } //調用函數: item3.getSiblings.call(items) //等價於item3.getSiblings() item3.addClass.call( item1,['text', 'red', 'blue']) //等價於item3.addClass( ['text', 'red', 'blue'])
特別注意:f.call ( asThis , input1 , input2 )
其中this是call()的第一個參數,即asThis會被看成this,而[ input1 ,input2]會被看成arguments學習
可是這樣的缺點是,當咱們調用時,若已聲明過這個函數,那該函數就會被覆蓋了。
2.構造新的接口,即無侵入
//javaScript window.jQuery = function(node){ return{ getSiblings : function(){ var allChlidren = this.parentNode.chlidren 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 }, addClass : function(classes){ classes.forEach( (value) => this.classList.add(value) ) } } } var item3 = jQuery(item3) //實現調用 item3.getSiblings() item3.addClass([' red '])
咱們還能夠優化window.$ = jQuery
。
小tips:當一個對象是由 $或jQuery構造出來的,你就在這個對象前也加上$,表示他是jQuery的對象,這樣就不會錯用成dom api了。即 var $node2 = $(node)。
咱們以上全部操做都是針對節點,那麼當傳入參數是非節點時該怎麼處理呢?咱們應該先對傳入參數進行判斷:若是傳入的是字符串,就用document.querySelectorAll()方法來選擇該節點;若是傳入的是節點,咱們也把它放入到僞數組中,這是由於querySelectorAll()返回的是僞數組,咱們要保持一致,後續才能使用一樣操做把兩個方法添加上去。
window.jQuery = function( nodeOrSelector ){ let nodes = {} if( typeof nodeOrSelector === 'string' ){ nodes = document.querySelectorAll(nodeOrSelector) }else if (nodeOrSelector instanceof Node){ nodes = { 0:nodeSelector , length:1 } } nodes.addClass = function (classes) { classes.forEach((value) => { for( let i=0 ; i< nodes.length ; i++ ){ nodes[i].classList.add(value) } }) } return nodes }
這樣,咱們就大體理解了jQuery的基本原理了,固然jQuery的做用不單單於此,後續實踐會讓咱們瞭解更多的。
<div id=x></div>
var div = document.getElementById('x');
var $div = $('#x');
請說出 div 和 $div 的聯繫和區別。
div 和 $div 的聯繫是:$(div) 能夠將 div 封裝成一個 jQuery 對象,就跟 $div 同樣$div[0] === div ,$div 的第一項就是 divdiv 和 $div 的區別是:div 的屬性和方法有 childNodes firstChid nodeType 等$div 的 屬性和方法有 addClass removeClass toggleClass 等