一個JavaScript庫。
接受一箇舊的節點,返回一個新的對象(哈希),這個對象就是jQuery對象。
jQuery的特色就是它擁有本身的API。
jQuery的源碼調用DOM API,可是jQuery對象沒法使用DOM API。javascript
jQuery極大地簡化了JS編程,也容易上手和學習。
儘量的避免了你使用原生DOM那些很「爛、複雜」的API。
jQuery的兼容性極好。
可是極多的API和不斷更新的時效性,想要透徹瞭解仍是離不開:
熟能生巧。html
就是編寫源碼,反覆優化實現需求的過程。java
這裏只舉一個「添加類」的函數爲例。在實際開發中,咱們要聲明不少的函數來實現方法的需求。
咱們使用簡單的html結構node
<body> <div>111</div> <div>222</div> <div>333</div> <ul> <li id="item1">select1</li> <li id="item2">select2</li> <li id="item3">select3</li> </ul> </body>
函數的功能:
爲指定節點(node)添加類。編程
function addClass(node,classes) { //給傳入的元素節點添加一個或多個類 classes.forEach((value)=>node.classList.add(value)) } addClass(item3,['a','b']) //id爲"item3"的節點 //<li id="item3" class="a b">select3</li>
上面的函數,雖然功能已經初步實現,可是有一個很大的問題:
咱們在處理複雜頁面時,爲了方便使用和記憶,可能會聲明不少名字和DOM API極爲「相似」的函數;那麼就會在不知不覺中,覆蓋掉一些API固有的函數,也可能會覆蓋一些全局變量。設計模式
DOM API將衆多方法放在document對象中(window.document.xxx
)。咱們也須要給它們一個命名空間,即將其寫入一個庫中,來暫時性的解決這個問題。也同時直接的告訴開發者:咱們聲明的這些函數,是有內在的關聯性的,由於它們都是在操縱節點。數組
命名空間:名字空間,也稱命名空間、名稱空間等,它表示着一個標識符的可見範圍。一個標識符可在多個名字空間中定義,它在不一樣名字空間中的含義是互不相干的。命名空間就是一種設計模式。(詳見維基百科)函數
好比筆者的 name = 'sgs'學習
window.sgs = {} //聲明一個空的庫 function addClass(){如上} sgs.addClass = addClass sgs.addClass(item2,['a','c']) //實現一樣的效果
這樣咱們就能夠調用sgs庫的方法來達到一樣的目的,而避免了全局變量的覆蓋;同時,這些方法都在咱們概括的同一個庫中。優化
這個問題解決之後,咱們又發現:咱們其實並不想這樣使用一個API。
咱們是否能夠直接的對節點調用一個功能函數:item3.addClass()
node.fn(x,y)
咱們來「篡改」Node的原型,給Node的公有屬性添加一個屬性。
把函數中的node所有替換爲this
,調用函數時默認把this
當作第一個參數傳入進來。
Node.prototype.addClass = function(classes) { classes.forEach((value)=>this.classList.add(value)) }
咱們能夠在Node.prototype中找到咱們本身添加的屬性。
使用方法時:
item3.addClass(['a','b','c']) //=== item3.addClass.call(item3,['a','b','c'])
可是,咱們這樣操做是在改Node的公有屬性。這樣就有很大的麻煩:
若是衆多開發者都在改公有屬性,那麼既會相互覆蓋,同時公有屬性也失去了自己最爲一個標準的意義。
如何再進一步的改進?
window.jQuery = function(node){ //傳入node節點 return { //key: value node.APIOne: function(){} node.APITwo: function(){} ... node.addClass: function(classes){ classes.forEach((value)=>node.classList.add(value)) } } }
咱們如今是這樣使用的
var nodex = jQuery(item3) //首先根據節點獲取一個新的對象 nodex.addClass(['a','b']) //使用jQuery提供的新API來操做
首先咱們把參數item3傳給了jQuery,而後jQuery將其存在了函數的node裏面;這樣咱們使用其餘方法時,能夠直接經過操做nodex這個對象來操做節點。
當你傳入的不是一個id對應的節點,而是表示一個節點的選擇器:
var nodex = jQuery('#item3')
讓jQuery源碼進行傳參類型的判斷:
window.jQuery = function(nodeOrSelector){ let node if(typeof nodeOrSelector === 'string'){ node = document.querySelector(nodeOrSelector) //若是jQuery發現你輸入了字符串,那麼就回去尋找這個字符串對應的節點 }else{ node = nodeOrSelector } return ... }
當你傳入一個選擇多個元素節點的選擇器時
var nodex = jQuery('ul>li') var nodey = jQuery('ul li:nth-child(2)')
將1~n個節點所有放在僞數組中。
window.jQuery = function (nodeOrSelector) { //--------------------判斷部分-------------------- let nodes = {} if (typeof nodeOrSelector === 'string') { let temp = document.querySelectorAll(nodeOrSelector) //僞數組,但它的原型鏈還覆蓋有不少層 for (let i = 0; i < temp.length; i++) { //獲取純淨的原型鏈,__proto__===Object.prototype nodes[i] = temp[i] } nodes.length = temp.length } else if (nodeOrSelector instanceof Node) { //說明傳入的是一個節點,可是保持一致性也要變成僞數組 nodes = { 0: nodeOrSelector, length: 1 } } //--------------------添加類方法-------------------- nodes.addClass = function () { let array = [] //傳入多個字符串表明類,不用傳入數組,內部將字符串組合爲數組 for (let k = 0; k < arguments.length; k++){ array.push(arguments[k]) } array.forEach((value) => { for (let i = 0; i < nodes.length; i++) { //nodes並非元素,而是僞數組 nodes[i].classList.add(value) } }) } //------------------設置節點文本方法------------------ nodes.setText = function (text) { //設置傳入參數的文本 for (let i = 0; i < nodes.length; i++) { nodes[i].textContent = text } } //------------------------------------------------- return nodes } //** 咱們同時引入$,經過$來聲明jQuery對象:** window.$ = jQuery
使用jQuery的兩個API:
var $div = $('div') var $li = $('ul>li') $div.addClass('active') $li.setText('We are the same')
至此,能夠看出,jQuery的本質就是一個JavaScript庫。經過一個「進化」的過程來達到一個使用的標準。jQuery的API就是源代碼中的屬性對應的函數,並且jQuery的源碼遠比此複雜和繁瑣,才得以實現如此強大的功能。
咱們能夠容許在不徹底透徹理解的狀況下使用一個原理,就像咱們引入外部的庫同樣。
咱們在此只是形象化的瞭解jQuery實現的基本原理。知根知底,才能夠更透徹的學習和使用jQuery。
Edit By: Eden Sheng
EMail: singlesaulwork@gmail.com