jQuery
整體框架甚是復(fù)雜,也不易讀懂,這幾日一直在研究這個(gè)笨重而強(qiáng)大的框架。jQuery
的總體架構(gòu)可以分為:入口模塊、底層模塊和功能模塊。這里,我們以jquery-1.7.1
為例進(jìn)行分析。
16 (function( window, undefined ) { // 構(gòu)造 jQuery 對(duì)象 22 var jQuery = (function() { 25 var jQuery = function( selector, context ) { 27 return new jQuery.fn.init( selector, context, rootjQuery ); 28 }, // 一堆局部變量聲明 97 jQuery.fn = jQuery.prototype = { 98 constructor: jQuery, 99 init: function( selector, context, rootjQuery ) { ... }, // 一堆原型屬性和方法 319 }; 322 jQuery.fn.init.prototype = jQuery.fn; 324 jQuery.extend = jQuery.fn.extend = function() { ... }; 388 jQuery.extend({ // 一堆靜態(tài)屬性和方法 892 }); 955 return jQuery; 957 })(); // 省略其他模塊的代碼 ...9246 window.jQuery = window.$ = jQuery;9266 })( window );
分析一下以上代碼,我們發(fā)現(xiàn)jquery
采取了匿名函數(shù)自執(zhí)行的寫法,這樣做的好處就是可以有效的防止命名空間與變量污染的問題
??s寫一下以上代碼就是:
(function(window, undefined) { var jQuery = function() {} // ... window.jQuery = window.$ = jQuery;})(window);
匿名函數(shù)傳了兩個(gè)參數(shù)進(jìn)來,一個(gè)是window
,一個(gè)是undefined
。我們知道,在js
中變量是有作用域鏈的,這兩個(gè)變量的傳入就會(huì)變成匿名函數(shù)的局部變量,訪問起來的時(shí)候速度會(huì)更快。通過傳入window
對(duì)象可以使window
對(duì)象作為局部變量使用,那么,函數(shù)的參數(shù)也都變成了局部變量,當(dāng)在jquery
中訪問window
對(duì)象時(shí),就不需要將作用域鏈退回到頂層作用域,從而可以更快的訪問window
對(duì)象。
js
在查找變量的時(shí)候,js引擎
首先會(huì)在函數(shù)自身的作用域中查找這個(gè)變量,如果沒有的話就繼續(xù)往上找,找到了就返回該變量,找不到就返回undefined
。undefined
是window
對(duì)象的一個(gè)屬性,通過傳入undefined
參數(shù),但又不進(jìn)行賦值,可以縮短查找undefined
時(shí)的作用域鏈。在 自調(diào)用匿名函數(shù) 的作用域內(nèi),確保undefined
是真的未定義。因?yàn)?code style="box-sizing: border-box; font-family: Consolas, Menlo, Monaco, 'Courier New', monospace; font-size: 0.92857em; padding: 2px 4px; color: rgb(199, 37, 78); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; background-color: rgb(246, 246, 246);">undefined能夠被重寫,賦予新的值。
jQuery.fn = jQuery.prototype = { constructor: jQuery, init: function( selector, context, rootjQuery ) { ... }, // 一堆原型屬性和方法 };
通過分析以上代碼,我們發(fā)現(xiàn)jQuery.fn
即是jQuery.prototype
,這樣寫的好處就是更加簡短吧。之后,我們又看到jquery
為了簡潔,干脆使用一個(gè)$
符號(hào)來代替jquery
使用,因此,在我們使用jquery
框架的使用經(jīng)常都會(huì)用到$()
,
jQuery
的對(duì)象并不是通過 new jQuery
創(chuàng)建的,而是通過 new jQuery.fn.init
創(chuàng)建的:
var jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context, rootjQuery );}
這里定義了一個(gè)變量jQuery
,他的值是jQuery
構(gòu)造函數(shù),在955行(最上面的代碼)返回并賦值給jQuery
變量
jQuery.fn
(上面97行)是構(gòu)造函數(shù)jQuery()
的原型對(duì)象,jQuery.fn.init()
是jQuery
原型方法,也可以稱作構(gòu)造函數(shù)。負(fù)責(zé)解析參數(shù)selector
和context
的類型并執(zhí)行相應(yīng)的查找。
參數(shù)context
:可以不傳入,或者傳入jQuery對(duì)象,DOM元素,普通js對(duì)象之一
參數(shù)rootjQuery
:包含了document
對(duì)象的jQuery
對(duì)象,用于document.getElementById()
查找失敗等情況。
jQuery.fn.init.prototype = jQuery.fn = jQuery.prototype
jQuery(selector [,context])
默認(rèn)情況下,對(duì)匹配元素的查找從根元素document
對(duì)象開始,即查找范圍是整個(gè)文檔樹,不過也可以傳入第二個(gè)參數(shù)context來限定它的查找范圍。例如:
$('div.foo').click(function () { $('span',this).addClass('bar');//限定查找范圍,即上面的context });
方法jQuery.extend(object)
和jQuery.fn.extend(object)
用于合并兩個(gè)或多個(gè)對(duì)象到第一個(gè)對(duì)象。相關(guān)源代碼如下(部分):
jQuery.extend = jQuery.fn.extend = function() { var options, name, src, copy, copyIsArray, clone,//定義的一組局部變量 target = arguments[0] || {}, i = 1, length = arguments.length, deep = false;
相關(guān)變量含義如下:
變量options:指向某個(gè)源對(duì)象變量name:表示目標(biāo)對(duì)象的某個(gè)屬性名變量src:表示目標(biāo)對(duì)象的某個(gè)屬性的原始值變量copy:表示某個(gè)源對(duì)象的某個(gè)屬性的值變量copyIsArray:指示變量copy是否是數(shù)組變量clone:表示深度復(fù)制時(shí)原始值的修正值變量target:指向目標(biāo)對(duì)象變量i:表示源對(duì)象的起始下標(biāo)變量length:表示參數(shù)的個(gè)數(shù),用于修正變量變量deep:指示是否執(zhí)行深度復(fù)制,默認(rèn)為false
jQuery.extend(object)
; 為jQuery
類添加添加類方法,可以理解為添加靜態(tài)方法。如:
$.extend({ add:function(a,b){ return a+b; } });
便為 jQuery
添加一個(gè)為add
的 “靜態(tài)方法”,之后便可以在引入 jQuery
的地方,使用這個(gè)方法了,
$.add(3,4); //return 7
jQuery.fn.extend(object)
,查看一段官網(wǎng)的代碼演示如下:
<label><input type="checkbox" name="foo"> Foo</label><label><input type="checkbox" name="bar"> Bar</label><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>
可以說,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 過濾類型 如果type是 > + ~ 空 四種關(guān)系選擇器中的一種,則跳過,在繼續(xù)過濾4 直到匹配到為 ID,CLASS,TAG 中一種 , 因?yàn)檫@樣才能通過瀏覽器的接口索取5 此時(shí)seed種子合集中就有值了,這樣把刷選的條件給縮的很小了6 如果匹配的seed的合集有多個(gè)就需要進(jìn)一步的過濾了,修正選擇器 selector: "div > p + div.aaron [type="checkbox"]"7 OK,跳到一下階段的編譯函數(shù)
開發(fā)網(wǎng)站的過程中,我們經(jīng)常遇到某些耗時(shí)很長的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開發(fā)團(tuán)隊(duì)就設(shè)計(jì)了deferred
對(duì)象。
簡單說,deferred
對(duì)象就是jQuery
的回調(diào)函數(shù)解決方案。在英語中,defer的意思是"延遲",所以deferred
對(duì)象的含義就是"延遲"到未來某個(gè)點(diǎn)再執(zhí)行。
回顧一下jQuery
的ajax
操作的傳統(tǒng)寫法:
$.ajax({ url: "test.html", success: function(){ alert("哈哈,成功了!"); }, error:function(){ alert("出錯(cuò)啦!"); } });
在上面的代碼中,$.ajax()
接受一個(gè)對(duì)象參數(shù),這個(gè)對(duì)象包含兩個(gè)方法:success
方法指定操作成功后的回調(diào)函數(shù),error方法指定操作失敗后的回調(diào)函數(shù)。
$.ajax()
操作完成后,如果使用的是低于1.5.0版本的jQuery,返回的是XHR對(duì)象,你沒法進(jìn)行鏈?zhǔn)讲僮?;如果高?.5.0版本,返回的是deferred對(duì)象,可以進(jìn)行鏈?zhǔn)讲僮鳌?/p>
現(xiàn)在,新的寫法是這樣的:
$.ajax("test.html") .done(function(){ alert("哈哈,成功了!"); }) .fail(function(){ alert("出錯(cuò)啦!"); });
deferred
對(duì)象的另一大好處,就是它允許你為多個(gè)事件指定一個(gè)回調(diào)函數(shù),這是傳統(tǒng)寫法做不到的。
請(qǐng)看下面的代碼,它用到了一個(gè)新的方法$.when()
:
$.when($.ajax("test1.html"), $.ajax("test2.html")) .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ù)。
內(nèi)部維護(hù)了三個(gè)回調(diào)函數(shù)列表:成功回調(diào)函數(shù)列表、失敗回調(diào)函數(shù)列表、消息回調(diào)函數(shù)列表,其他方法則圍繞這三個(gè)列表進(jìn)行操作和檢測。
jQuery.Deferred( func )
的源碼結(jié)構(gòu):
jQuery.extend({ 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)來執(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)用。/*請(qǐng)求 '/when.do?method=when1' 返回 {"when":1}請(qǐng)求 '/when.do?method=when2' 返回 {"when":2}請(qǐng)求 '/when.do?method=when3' 返回 {"when":3}*/var whenDone = function(){ console.log( 'done', arguments ); }, whenFail = function(){ console.log( 'fail', arguments ); };$.when( $.ajax( '/when.do?method=when1', { dataType: "json" } ), $.ajax( '/when.do?method=when2', { dataType: "json" } ), $.ajax( '/when.do?method=when3', { dataType: "json" } )).done( whenDone ).fail( whenFail );
Deferred
解耦異步任務(wù)和回調(diào)函數(shù)
為 ajax 模塊、隊(duì)列模塊、ready
事件提供基礎(chǔ)功能。
原型屬性和方法源代碼:
97 jQuery.fn = jQuery.prototype = { 98 constructor: jQuery, 99 init: function( selector, context, rootjQuery ) {} 210 selector: "", 213 jquery: "1.7.1", 216 length: 0, 219 size: function() {}, 223 toArray: function() {}, 229 get: function( num ) {}, 241 pushStack: function( elems, name, selector ) {}, 270 each: function( callback, args ) {}, 274 ready: function( fn ) {}, // 284 eq: function( i ) {}, 291 first: function() {}, 295 last: function() {}, 299 slice: function() {}, 304 map: function( callback ) {}, 310 end: function() {}, 316 push: push, 317 sort: [].sort, 318 splice: [].splice 319 };
屬性selector
用于記錄jQuery
查找和過濾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)樗麤]有函數(shù)調(diào)用開銷。
.size()
源碼如下:
size():function(){ return this.length;}
方法.toArray()
將當(dāng)前jQuery
對(duì)象轉(zhuǎn)換為真正的數(shù)組,轉(zhuǎn)換后的數(shù)組包含了所有元素,其源碼如下:
toArray: function() { return slice.call( this ); },
方法.get(index)
返回當(dāng)前jQuery對(duì)象中指定位置的元素,或包含了全部元素的數(shù)組。其源
碼如下:
get: function( num ) { return num == null ? // Return a 'clean' array this.toArray() : // Return just the object ( num < 0 ? this[ this.length + num ] : this[ num ] ); },
如果沒有傳入?yún)?shù),則調(diào)用.toArray()
返回了包含有鎖元素的數(shù)組;如果指定了參數(shù)index,則返回一個(gè)單獨(dú)的元素,index
從0開始計(jì)數(shù),并且支持負(fù)數(shù)。
首先會(huì)判斷num
是否小于0,如果小于0,則用length+num
重新計(jì)算下標(biāo),然后使用數(shù)組訪問操作符([]
)獲取指定位置的元素,這是支持下標(biāo)為負(fù)數(shù)的一個(gè)小技巧;如果大于等于0,直接返回指定位置的元素。
eg()和get()
使用詳解:http://segmentfault.com/blog/trigkit4/1190000000660257#articleHeader3
方法.each()用于遍歷當(dāng)前jQuery
對(duì)象,并在每個(gè)元素上執(zhí)行回調(diào)函數(shù)。方法.each()
內(nèi)部通過簡單的調(diào)用靜態(tài)方法jQuery.each()
實(shí)現(xiàn):
each: function( callback, args ) { return jQuery.each( this, callback, args ); },
回調(diào)函數(shù)是在當(dāng)前元素為上下文的語境中觸發(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元素集合的值。
map: function( callback ) { return this.pushStack( jQuery.map(this, function( elem, i ) { return callback.call( elem, i, elem ); })); },
原型方法.pushStack()
創(chuàng)建一個(gè)新的空jQuery
對(duì)象,然后把DOM元素集合放入這個(gè)jQuery
對(duì)象中,并保留對(duì)當(dāng)前jQuery
對(duì)象的引用。
原型方法.pushStack()
是核心方法之一,它為以下方法提供支持:
jQuery對(duì)象遍歷:.eq()、.first()、.last()、.slice()、.map()。DOM查找、過濾:.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ù)組(或類數(shù)組對(duì)象)。參數(shù)name:產(chǎn)生元素?cái)?shù)組elems的jQuery方法名。參數(shù)selector:傳給jQuery方法的參數(shù),用于修正原型屬性.selector。
方法.end()結(jié)束當(dāng)前鏈條中最近的篩選操作,并將匹配元素還原為之前的狀態(tài)
end: function() { return this.prevObject || this.constructor(null); },
返回前一個(gè)jQuery
對(duì)象,如果屬性prevObect
不存在,則構(gòu)建一個(gè)空的jQuery
對(duì)象返回。方法.pushStack()
用于入棧,方法.end()
用于出棧
相關(guān)源碼如下:
388 jQuery.extend({ 389 noConflict: function( deep ) {}, 402 isReady: false, 406 readyWait: 1, 409 holdReady: function( hold ) {}, 418 ready: function( wait ) {}, 444 bindReady: function() {}, 492 isFunction: function( obj ) {}, 496 isArray: Array.isArray || function( obj ) {}, 501 isWindow: function( obj ) {}, 505 isNumeric: function( obj ) {}, 509 type: function( obj ) {}, 515 isPlainObject: function( obj ) {}, 544 isEmptyObject: function( obj ) {}, 551 error: function( msg ) {}, 555 parseJSON: function( data ) {}, 581 parseXML: function( data ) {}, 601 noop: function() {}, 606 globalEval: function( data ) {}, 619 camelCase: function( string ) {}, 623 nodeName: function( elem, name ) {}, 628 each: function( object, callback, args ) {}, 669 trim: trim ? function( text ) {} : function( text ) {}, 684 makeArray: function( array, results ) {}, 702 inArray: function( elem, array, i ) {}, 724 merge: function( first, second ) {}, 744 grep: function( elems, callback, inv ) {}, 761 map: function( elems, callback, arg ) {}, 794 guid: 1, 798 proxy: function( fn, context ) {}, 825 access: function( elems, key, value, exec, fn, pass ) {}, 852 now: function() {}, 858 uaMatch: function( ua ) {}, 870 sub: function() {}, 891 browser: {} 892 });
方法jQuery.noConflict([removeAll])
用于釋放jQuery
對(duì)全局變量$的控制權(quán),可選參數(shù)removeAll
指示是否釋放對(duì)全局變量jQuery
的控制權(quán)。
在使用jQuery
的同時(shí),如果需要使用另一個(gè)javascript
庫,可以調(diào)用$.noConflict()
返回$
給其他庫
方法jQuery.isFunction(obj)
用于判斷傳入的參數(shù)是否是函數(shù);
方法jQuery.isArray(obj)
用于判斷傳入的參數(shù)是否是數(shù)組。
這兩個(gè)方法的實(shí)現(xiàn)依賴于jQuer.type(obj)
,通過判斷jQuery.type(obj)
返回值是否是function
和array
來實(shí)現(xiàn)
相關(guān)源碼如下:
492 isFunction : function(obj){493 return jQuery.type(obj) === "function";494 },495 496 isArray: Array.isArray || function(obj){497 return jQuery.type(obj) === "array";498 },
方法jQuery.type(obj)
用于判斷參數(shù)的內(nèi)建JavaScript
類型。如果參數(shù)是undefined
或 null
,返回 undefined
或null
;如果參數(shù)是JavaScript
內(nèi)部對(duì)象,則返回對(duì)象
的字符串名稱;其他情況一律返回object
,相關(guān)源碼如下:
509 type :function(obj){510 return obj == null ?511 String( obj ) :512 class2type[toString.call(obj)] || "object";513 },
方法jQuery.isWindow(obj)
用于判斷傳入的參數(shù)是否是window
對(duì)象,通過檢測是否存在特征屬性setInterval
來實(shí)現(xiàn),相關(guān)代碼如下:
501 isWindow: function(obj){502 return obj && typeof obj ==="object" && "setInterval" in obj;503 },
聯(lián)系客服