jQuery整體框架甚是復(fù)雜,也不易讀懂,這幾日一直在研究這個(gè)笨重而強(qiáng)大的框架。jQuery的總體架構(gòu)可以分為:入口模塊、底層模塊和功能模塊。這里,我們以jquery-1.7.1為例進(jìn)行分析。
jquery的總體架構(gòu)
分析一下以上代碼,我們發(fā)現(xiàn)jquery采取了匿名函數(shù)自執(zhí)行的寫(xiě)法,這樣做的好處就是可以有效的防止命名空間與變量污染的問(wèn)題。縮寫(xiě)一下以上代碼就是:
參數(shù)window
匿名函數(shù)傳了兩個(gè)參數(shù)進(jìn)來(lái),一個(gè)是window,一個(gè)是undefined。我們知道,在js中變量是有作用域鏈的,這兩個(gè)變量的傳入就會(huì)變成匿名函數(shù)的局部變量,訪問(wèn)起來(lái)的時(shí)候速度會(huì)更快。通過(guò)傳入window對(duì)象可以使window對(duì)象作為局部變量使用,那么,函數(shù)的參數(shù)也都變成了局部變量,當(dāng)在jquery中訪問(wèn)window對(duì)象時(shí),就不需要將作用域鏈退回到頂層作用域,從而可以更快的訪問(wèn)window對(duì)象。
參數(shù)undefined
js在查找變量的時(shí)候,js引擎首先會(huì)在函數(shù)自身的作用域中查找這個(gè)變量,如果沒(méi)有的話就繼續(xù)往上找,找到了就返回該變量,找不到就返回undefined。undefined是window對(duì)象的一個(gè)屬性,通過(guò)傳入undefined參數(shù),但又不進(jìn)行賦值,可以縮短查找undefined時(shí)的作用域鏈。在 自調(diào)用匿名函數(shù) 的作用域內(nèi),確保undefined是真的未定義。因?yàn)閡ndefined能夠被重寫(xiě),賦予新的值。
jquery.fn是啥?
通過(guò)分析以上代碼,我們發(fā)現(xiàn)jQuery.fn即是jQuery.prototype,這樣寫(xiě)的好處就是更加簡(jiǎn)短吧。之后,我們又看到j(luò)query為了簡(jiǎn)潔,干脆使用一個(gè)$符號(hào)來(lái)代替jquery使用,因此,在我們使用jquery框架的使用經(jīng)常都會(huì)用到$(),
構(gòu)造函數(shù)jQuery()
圖片描述
jQuery的對(duì)象并不是通過(guò) new jQuery 創(chuàng)建的,而是通過(guò) new jQuery.fn.init 創(chuàng)建的:
return new jQuery.fn.init( selector, context, rootjQuery );
}
這里定義了一個(gè)變量jQuery,他的值是jQuery構(gòu)造函數(shù),在955行(最上面的代碼)返回并賦值給jQuery變量
jQuery.fn.init
jQuery.fn (上面97行)是構(gòu)造函數(shù)jQuery()的原型對(duì)象,jQuery.fn.init()是jQuery原型方法,也可以稱(chēng)作構(gòu)造函數(shù)。負(fù)責(zé)解析參數(shù)selector和context的類(lèi)型并執(zhí)行相應(yīng)的查找。
參數(shù)context:可以不傳入,或者傳入jQuery對(duì)象,DOM元素,普通js對(duì)象之一
參數(shù)rootjQuery:包含了document對(duì)象的jQuery對(duì)象,用于document.getElementById()查找失敗等情況。
默認(rèn)情況下,對(duì)匹配元素的查找從根元素document 對(duì)象開(kāi)始,即查找范圍是整個(gè)文檔樹(shù),不過(guò)也可以傳入第二個(gè)參數(shù)context來(lái)限定它的查找范圍。例如:
方法jQuery.extend(object)和jQuery.fn.extend(object)用于合并兩個(gè)或多個(gè)對(duì)象到第一個(gè)對(duì)象。相關(guān)源代碼如下(部分):
jQuery.extend(object); 為jQuery類(lèi)添加添加類(lèi)方法,可以理解為添加靜態(tài)方法。如:
便為 jQuery 添加一個(gè)為add 的 “靜態(tài)方法”,之后便可以在引入 jQuery 的地方,使用這個(gè)方法了,
$.add(3,4); //return 7
jQuery.fn.extend(object),查看一段官網(wǎng)的代碼演示如下:
<script>
jQuery.fn.extend({
check: function() {
return this.each(function() {
this.checked = true;
});
},
uncheck: function() {
return this.each(function() {
this.checked = false;
});
}
});
// Use the newly created .check() method
$( "input[type='checkbox']" ).check();
</script>
CSS選擇器引擎 Sizzle
可以說(shuō),jQuery是為操作DOM而誕生的,jQuery之所以如此強(qiáng)大,得益于CSS選擇器引擎 Sizzle,解析規(guī)則引用網(wǎng)上的一段實(shí)例:
selector:"div > p + div.aaron input[type="checkbox"]"
解析規(guī)則:
1 按照從右到左
2 取出最后一個(gè)token 比如[type="checkbox"]
{
matches : Array[3]
type : "ATTR"
value : "[type="
checkbox "]"
}
3 過(guò)濾類(lèi)型 如果type是 > + ~ 空 四種關(guān)系選擇器中的一種,則跳過(guò),在繼續(xù)過(guò)濾
4 直到匹配到為 ID,CLASS,TAG 中一種 , 因?yàn)檫@樣才能通過(guò)瀏覽器的接口索取
5 此時(shí)seed種子合集中就有值了,這樣把刷選的條件給縮的很小了
6 如果匹配的seed的合集有多個(gè)就需要進(jìn)一步的過(guò)濾了,修正選擇器 selector: "div > p + div.aaron [type="checkbox"]"
7 OK,跳到一下階段的編譯函數(shù)
deferred對(duì)象
開(kāi)發(fā)網(wǎng)站的過(guò)程中,我們經(jīng)常遇到某些耗時(shí)很長(zhǎng)的javascript操作。其中,既有異步的操作(比如ajax讀取服務(wù)器數(shù)據(jù)),也有同步的操作(比如遍歷一個(gè)大型數(shù)組),它們都不是立即能得到結(jié)果的。
通常的做法是,為它們指定回調(diào)函數(shù)(callback)。即事先規(guī)定,一旦它們運(yùn)行結(jié)束,應(yīng)該調(diào)用哪些函數(shù)。
但是,在回調(diào)函數(shù)方面,jQuery的功能非常弱。為了改變這一點(diǎn),jQuery開(kāi)發(fā)團(tuán)隊(duì)就設(shè)計(jì)了deferred對(duì)象。
簡(jiǎn)單說(shuō),deferred對(duì)象就是jQuery的回調(diào)函數(shù)解決方案。在英語(yǔ)中,defer的意思是"延遲",所以deferred對(duì)象的含義就是"延遲"到未來(lái)某個(gè)點(diǎn)再執(zhí)行。
回顧一下jQuery的ajax操作的傳統(tǒng)寫(xiě)法:
$.ajax()操作完成后,如果使用的是低于1.5.0版本的jQuery,返回的是XHR對(duì)象,你沒(méi)法進(jìn)行鏈?zhǔn)讲僮鳎蝗绻哂?.5.0版本,返回的是deferred對(duì)象,可以進(jìn)行鏈?zhǔn)讲僮鳌?/p>
現(xiàn)在,新的寫(xiě)法是這樣的:
deferred對(duì)象的另一大好處,就是它允許你為多個(gè)事件指定一個(gè)回調(diào)函數(shù),這是傳統(tǒng)寫(xiě)法做不到的。
請(qǐng)看下面的代碼,它用到了一個(gè)新的方法$.when():
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出錯(cuò)啦!"); });
這段代碼的意思是,先執(zhí)行兩個(gè)操作$.ajax("test1.html")和$.ajax("test2.html"),如果都成功了,就運(yùn)行done()指定的回調(diào)函數(shù);如果有一個(gè)失敗或都失敗了,就執(zhí)行fail()指定的回調(diào)函數(shù)。
jQuery.Deferred( func ) 的實(shí)現(xiàn)原理
內(nèi)部維護(hù)了三個(gè)回調(diào)函數(shù)列表:成功回調(diào)函數(shù)列表、失敗回調(diào)函數(shù)列表、消息回調(diào)函數(shù)列表,其他方法則圍繞這三個(gè)列表進(jìn)行操作和檢測(cè)。
jQuery.Deferred( func ) 的源碼結(jié)構(gòu):
Deferred: function( func ) {
// 成功回調(diào)函數(shù)列表
var doneList = jQuery.Callbacks( "once memory" ),
// 失敗回調(diào)函數(shù)列表
failList = jQuery.Callbacks( "once memory" ),
// 消息回調(diào)函數(shù)列表
progressList = jQuery.Callbacks( "memory" ),
// 初始狀態(tài)
state = "pending",
// 異步隊(duì)列的只讀副本
promise = {
// done, fail, progress
// state, isResolved, isRejected
// then, always
// pipe
// promise
},
// 異步隊(duì)列
deferred = promise.promise({}),
key;
// 添加觸發(fā)成功、失敗、消息回調(diào)函列表的方法
for ( key in lists ) {
deferred[ key ] = lists[ key ].fire;
deferred[ key + "With" ] = lists[ key ].fireWith;
}
// 添加設(shè)置狀態(tài)的回調(diào)函數(shù)
deferred.done( function() {
state = "resolved";
}, failList.disable, progressList.lock )
.fail( function() {
state = "rejected";
}, doneList.disable, progressList.lock );
// 如果傳入函數(shù)參數(shù) func,則執(zhí)行。
if ( func ) {
func.call( deferred, deferred );
}
// 返回異步隊(duì)列 deferred
return deferred;
},
}
jQuery.when( deferreds )
提供了基于一個(gè)或多個(gè)對(duì)象的狀態(tài)來(lái)執(zhí)行回調(diào)函數(shù)的功能,通常是基于具有異步事件的異步隊(duì)列。
jQuery.when( deferreds ) 的用法
如果傳入多個(gè)異步隊(duì)列對(duì)象,方法 jQuery.when() 返回一個(gè)新的主異步隊(duì)列對(duì)象的只讀副本,只讀副本將跟蹤所傳入的異步隊(duì)列的最終狀態(tài)。
一旦所有異步隊(duì)列都變?yōu)槌晒顟B(tài),“主“異步隊(duì)列的成功回調(diào)函數(shù)被調(diào)用;
如果其中一個(gè)異步隊(duì)列變?yōu)槭顟B(tài),主異步隊(duì)列的失敗回調(diào)函數(shù)被調(diào)用。
圖片描述
異步隊(duì)列 Deferred
解耦異步任務(wù)和回調(diào)函數(shù)
為 ajax 模塊、隊(duì)列模塊、ready 事件提供基礎(chǔ)功能。
原型屬性和方法
原型屬性和方法源代碼:
屬性selector用于記錄jQuery查找和過(guò)濾DOM元素時(shí)的選擇器表達(dá)式。
屬性.length表示當(dāng)前jquery對(duì)象中元素的個(gè)數(shù)。
方法.size()返回當(dāng)前jquery對(duì)象中元素的個(gè)數(shù),功能上等同于屬性length,但應(yīng)該優(yōu)先使用length,因?yàn)樗麤](méi)有函數(shù)調(diào)用開(kāi)銷(xiāo)。
.size()源碼如下:
方法.toArray()將當(dāng)前jQuery對(duì)象轉(zhuǎn)換為真正的數(shù)組,轉(zhuǎn)換后的數(shù)組包含了所有元素,其源碼如下:
方法.get(index)返回當(dāng)前jQuery對(duì)象中指定位置的元素,或包含了全部元素的數(shù)組。其源
碼如下:
// Return a 'clean' array
this.toArray() :
// Return just the object
( num < 0 ? this[ this.length + num ] : this[ num ] );
},
首先會(huì)判斷num是否小于0,如果小于0,則用length+num重新計(jì)算下標(biāo),然后使用數(shù)組訪問(wèn)操作符([])獲取指定位置的元素,這是支持下標(biāo)為負(fù)數(shù)的一個(gè)小技巧;如果大于等于0,直接返回指定位置的元素。
eg()和get()使用詳解:jquery常用方法及使用示例匯總
方法.each()用于遍歷當(dāng)前jQuery對(duì)象,并在每個(gè)元素上執(zhí)行回調(diào)函數(shù)。方法.each()內(nèi)部通過(guò)簡(jiǎn)單的調(diào)用靜態(tài)方法jQuery.each()實(shí)現(xiàn):
回調(diào)函數(shù)是在當(dāng)前元素為上下文的語(yǔ)境中觸發(fā)的,即關(guān)鍵字this總是指向當(dāng)前元素,在回調(diào)函數(shù)中return false 可以終止遍歷。
方法.map()遍歷當(dāng)前jQuery對(duì)象,在每個(gè)元素上執(zhí)行回調(diào)函數(shù),并將回調(diào)函數(shù)的返回值放入一個(gè)新jQuery對(duì)象中。該方法常用于獲取或設(shè)置DOM元素集合的值。
原型方法.pushStack()創(chuàng)建一個(gè)新的空jQuery對(duì)象,然后把DOM元素集合放入這個(gè)jQuery對(duì)象中,并保留對(duì)當(dāng)前jQuery對(duì)象的引用。
原型方法.pushStack()是核心方法之一,它為以下方法提供支持:
jQuery對(duì)象遍歷:.eq()、.first()、.last()、.slice()、.map()。
DOM查找、過(guò)濾:.find()、.not()、.filter()、.closest()、.add()、.andSelf()。
DOM遍歷:.parent()、.parents()、.parentsUntil()、.next()、.prev()、.nextAll()、.prevAll()、.nextUnit()、.prevUnit()、.siblings()、.children()、.contents()。
DOM插入:jQuery.before()、jQuery.after()、jQuery.replaceWith()、.append()、.prepent()、.before()、.after()、.replaceWith()。
定義方法.push( elems, name, selector ),它接受3個(gè)參數(shù):
參數(shù)elems:將放入新jQuery對(duì)象的元素?cái)?shù)組(或類(lèi)數(shù)組對(duì)象)。
參數(shù)name:產(chǎn)生元素?cái)?shù)組elems的jQuery方法名。
參數(shù)selector:傳給jQuery方法的參數(shù),用于修正原型屬性.selector。
方法.end()結(jié)束當(dāng)前鏈條中最近的篩選操作,并將匹配元素還原為之前的狀態(tài)
返回前一個(gè)jQuery對(duì)象,如果屬性prevObect不存在,則構(gòu)建一個(gè)空的jQuery對(duì)象返回。方法.pushStack()用于入棧,方法.end()用于出棧
靜態(tài)屬性和方法
相關(guān)源碼如下:
未完待續(xù)、、、今天就先到這里了,下次補(bǔ)齊。別急哈小伙伴們
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注