
jquery技術内幕:深入解析jquery架構設計與實作原理
高 雲 著
圖書在版編目(cip)資料
jquery技術内幕:深入解析jquery架構設計與實作原理 / 高雲著. —北京:機械工業出版社,2013.11
isbn 978-7-111-44082-6
i. j… ii. 高… iii. java語言-程式設計 iv. tp312
中國版本圖書館cip資料核字(2013)第221662号
版權所有·侵權必究
封底無防僞标均為盜版
本書法律顧問 北京市展達律師事務所
本書由阿裡巴巴資深前端開發工程師撰寫,從源代碼角度全面而系統地解讀了jquery的17個子產品的架構設計理念和内部實作原理,旨在幫助讀者參透jquery中的實作技巧和技術精髓,同時本書也對廣大開發者如何通過閱讀源代碼來提升編碼能力和軟體架構能力提供了指導。
本書首先通過“總體架構”梳理了各個子產品的分類、功能和依賴關系,讓大家對jquery的工作原理有大緻的印象;進而通過“構造jquery對象”章節分析了構造函數jquery()的各種用法和内部構造過程;接着詳細分析了底層支援子產品的源碼實作,包括:選擇器sizzle、異步隊列deferred、資料緩存data、隊列queue、浏覽器功能測試support;最後詳細分析了功能子產品的源碼實作,包括:屬性操作attributes、事件系統events、dom周遊traversing、dom操作manipulation、樣式操作css、異步請求ajax、動畫effects。
本書在分析每個子產品時均采用由淺入深的方式,先概述功能、用法、結構和實作原理,然後介紹關鍵步驟和分析源碼實作。讓讀者不僅知其然,而且知其是以然。事實上,本書的根本價值在于傳達一種通過閱讀源碼快速成長的方式。無論是前端新人,還是經驗豐富的老手,隻要是對javascript感興趣的開發人員,都會從本書中受益。
機械工業出版社(北京市西城區百萬莊大街22号 郵政編碼 100037)
責任編輯:朱秀英
印刷
2014年1月第1版第1次印刷
186mm×240mm ? 39.5印張
标準書号:isbn 978-7-111-44082-6
定 價:99.00元
凡購本書,如有缺頁、倒頁、脫頁,由本社發行部調換
客服熱線:(010)88378991 88361066 投稿熱線:(010)88379604
購書熱線:(010)68326294 88379649 68995259 讀者信箱:[email protected]
前 言
jquery是業界最流行的javascript庫,其api非常精緻和優雅,但是jquery的源碼卻龐大且晦澀難懂,在本書開始寫作時釋出的1.7.1版本有9266行代碼,涉及17個子產品,讀起來常常是一頭霧水、有心無力。本書嘗試對jquery的源碼進行系統、完整的介紹和分析,闡述jquery的設計理念、實作原理和源碼實作。
為什麼要寫這本書
筆者在2010年參與了一款衛星機頂盒使用者界面的設計和開發,程式運作在機頂盒中間件供應商提供的一款定制浏覽器上,在開發過程中,發現這款浏覽器的行為類似于古老的ie 5,各種缺陷和bug折磨得筆者苦不堪言,是以希望引入jquery作為基礎庫,并開發一些通用元件和接口來簡化開發過程,可是很快又發現這款浏覽器對正規表達式的支援非常粗糙,導緻選擇器引擎sizzle根本無法運作。此時,對jquery進行簡單改造已經滿足不了需求。
然而令人驚豔的是,這款浏覽器提供了與作業系統、檔案系統、中間件、播放器、智能卡和衛星接收器等互動的javascript api,例如,待機&關機、檔案讀寫、計費、衛星鎖頻、資料接收等。鑒于這種複雜的體系架構,以及對浏覽器缺陷的完善也非短期可以完成,筆者開始為這款機頂盒浏覽器移植jquery,進而開始了對jquery源碼的學習和分析。
從2011年6月開始,筆者開始把心得和記錄整理成《jquery 1.6.1源碼分析系列》,陸續發表在程式員社群iteye和部落格園上,本書最初的内容也是基于這個系列而來的。《jquery 1.6.1源碼分析系列》成體系但尚粗糙不堪,是以本書基于jquery 1.7.1幾乎全部重寫,在内容上更加完善和嚴謹。
希望本書對讀者能有所幫助。
讀者對象
本書适合初級、中級、進階前端開發工程師,以及對前端開發感興趣的讀者。
在閱讀本書之前,讀者應該初步掌握javascript、html、css的基礎知識,初步掌握jquery的使用,或者有其他語言基礎。
如何閱讀本書
本書共分為四大部分,首先介紹了jquery的總體架構,然後分别分析了構造jquery對象子產品、底層支援子產品和功能子產品的源碼實作。在閱讀本書時,首先建議讀者建立一個源碼閱讀和調試環境,在閱讀過程中進行各種嘗試和驗證,加深對源碼的了解;在閱讀本書的每個章節前,建議讀者先仔細閱讀相應的官方文檔,并驗證官方示例,掌握api的功能和用法。
第一部分(第1章)對jquery的設計理念、總體架構和源碼結構進行了介紹和分析,讓讀者對jquery有整體的認識。
第二部分(第2章)詳細介紹和分析了構造函數jquery()的用法、構造過程、原型屬性和方法、靜态屬性和方法。
第三部分(第3~7章)詳細分析了底層支援子產品的源碼實作,包括選擇器sizzle、異步隊列deferred object、資料緩存data、隊列queue、浏覽器功能測試support。
第四部分(第8~14章)詳細分析了功能子產品的源碼實作,包括屬性操作attributes、事件系統events、dom周遊traversing、dom操作manipulation、樣式操作css、異步請求ajax、動畫effects。
勘誤和支援
由于筆者水準有限,再加上寫作時的疏漏,書中難免存在許多需要改進之處。在此,歡迎讀者朋友們指出書中存在的問題,并提出指導性意見,不勝感謝。送出位址為https://github.com/nuysoft/jquery-errata-support/issues,勘誤内容将在http://nuysoft.com/jquery.html上釋出。
緻謝
首先要向jquery作者john resig、jquery團隊和jquery社群緻敬,是你們繁榮了javascript。
感謝機械工業出版社的楊繡國(lisa)編輯,她對本書進行了大量潤色,修訂和批注的内容比我寫的内容要多得多。本書的進度和内容在寫作期間不斷變更,因為她的不斷激勵和反複修訂,才使本書得以呈現在廣大讀者面前。感謝朱秀英編輯,她修正了書稿中諸多不完善之處。感謝為本書拾遺補缺的諸多幕後編輯。
感謝為本書初稿給出回報意見的所有人:古西風(葉克良)、左莫(徐波)、逸才(陳養劍)、崇志(李德強)、李志博、王陽光、符寶(徐麗麗)、餘鵬、許傑。你們的寶貴意見使本書内容更加完善、更加準确。
本書最初的内容以及得到出版的機遇,源自發表在iteye和部落格園的一系列部落格,感謝這兩個社群,以及是以結識的朋友們。
高雲(nuysoft)
目 錄
前言
第一部分 總體架構
第1章 總體架構 2
1.1 設計理念 2
1.2 總體架構 2
1.3 自調用匿名函數 4
1.4 總結 6
第二部分 構造jquery對象
第2章 構造jquery對象 8
2.1 構造函數jquery() 8
2.1.1 jquery( selector [, context] ) 9
2.1.2 jquery( html [, ownerdocument] )、jquery( html, props ) 9
2.1.3 jquery( element )、jquery( elementarray ) 10
2.1.4 jquery( object ) 10
2.1.5 jquery( callback ) 11
2.1.6 jquery( jquery object ) 11
2.1.7 jquery() 11
2.2 總體結構 11
2.3 jquery.fn.init( selector, context, rootjquery ) 13
2.3.1 12個分支 13
2.3.2 源碼分析 14
2.3.3 小結 21
2.4 jquery.buildfragment( args, nodes, scripts ) 22
2.4.1 實作原理 22
2.4.2 源碼分析 22
2.4.3 小結 26
2.5 jquery.clean( elems, context, fragment, scripts ) 27
2.5.1 實作原理 27
2.5.2 源碼分析 27
2.5.3 小結 39
2.6 jquery.extend()、jquery.fn.extend() 40
2.6.1 如何使用 40
2.6.2 源碼分析 40
2.7 原型屬性和方法 43
2.7.1 .selector、.jquery、.length、.size() 44
2.7.2 .toarray()、.get( [index] ) 45
2.7.3 .each( function(index, element) )、jquery.each( collection, callback (indexinarray, valueofelement) ) 46
2.7.4 .map( callback(index, domelement) )、jquery.map( arrayorobject, callback(value, indexorkey) ) 47
2.7.5 .pushstack( elements, name, arguments ) 49
2.7.6 .end() 51
2.7.7 .eq( index )、.first()、.last()、.slice( start [, end] ) 51
2.7.8 .push( value, ... )、.sort( [orderfunc] )、.splice( start,deletecount, value, ... ) 52
2.7.9 小結 53
2.8 靜态屬性和方法 54
2.8.1 jquery.noconflict( [removeall] ) 55
2.8.2 類型檢測:jquery.isfunction( obj )、jquery.isarray( obj )、jquery.iswindow( obj )、jquery.isnumeric( value )、jquery.type( obj )、jquery.isplainobject( object )、jquery.isemptyobject( object ) 56
2.8.3 解析json和xml:jquery.parsejson( data )、jquery.parsexml( data ) 60
2.8.4 jquery.globaleval( code ) 65
2.8.5 jquery.camelcase( string ) 65
2.8.6 jquery.nodename( elem, name ) 66
2.8.7 jquery.trim( str ) 67
2.8.8 數組操作方法:jquery.makearray( obj )、jquery.inarray( value, array [, fromindex] )、jquery.merge( first, second )、jquery.grep( array, function(elementofarray, indexinarray) [, invert] ) 68
2.8.9 jquery.guid、jquery.proxy( function, context ) 72
2.8.10 jquery.access( elems, key, value, exec, fn( elem, key, value ), pass ) 74
2.8.11 jquery.error( message )、jquery.noop()、jquery.now() 75
2.8.12 浏覽器嗅探:jquery.uamatch( ua )、jquery.browser 76
2.8.13 小結 77
2.9 總結 77
第三部分 底層支援子產品
第3章 選擇器sizzle 80
3.1 總體結構 81
3.2 選擇器表達式 83
3.3 設計思路 84
3.4 sizzle( selector, context, results, seed ) 86
3.5 正則chunker 94
3.6 sizzle.find( expr, context, isxml ) 94
3.7 sizzle.filter( expr, set, inplace, not ) 99
3.8 sizzle.selectors.relative 103
3.8.1 "+" 105
3.8.2 ">" 106
3.8.3 "" 108
3.8.4 "~" 108
3.8.5 dircheck( dir, cur, donename, checkset, nodecheck, isxml ) 109
3.8.6 dirnodecheck( dir, cur, donename, checkset, nodecheck, isxml ) 111
3.9 sizzle.selectors 112
3.9.1 sizzle.selectors.order 112
3.9.2 sizzle.selectors.match/leftmatch 113
3.9.3 sizzle.selectors.find 122
3.9.4 sizzle.selectors.prefilter 123
3.9.5 sizzle.selectors.filters 129
3.9.6 sizzle.selectors.setfilters 132
3.9.7 sizzle.selectors.filter 133
3.10 工具方法 140
3.10.1 sizzle.uniquesort( results ) 140
3.10.2 sortorder( a, b ) 141
3.10.3 sizzle.contains( a, b ) 144
3.10.4 sizzle.error( msg ) 145
3.10.5 sizzle.gettext( elem ) 145
3.11 便捷方法 146
3.11.1 sizzle.matches( expr, set ) 146
3.11.2 sizzle.matchesselector( node, expr ) 146
3.12 jquery擴充 147
3.12.1 暴露sizzle給jquery 147
3.12.2 .find( selector ) 148
3.12.3 .has( target ) 149
3.12.4 .not( selector )、.filter( selector ) 150
3.12.5 .is( selector ) 152
3.12.6 .closest( selectors, context ) 153
3.12.7 .index( elem ) 154
3.12.8 .add( selector, context ) 155
3.12.9 jquery.filter( expr, elems, not ) 156
3.12.10 :animated 157
3.12.11 hidden、:visible 157
3.13 總結 158
第4章 異步隊列deferred object 160
4.1 jquery.callbacks( flags ) 161
4.1.1 實作原理和總體結構 162
4.1.2 源碼分析 163
4.1.3 小結 174
4.2 jquery.deferred( func ) 174
4.2.1 實作原理和總體結構 176
4.2.2 源碼分析 177
4.2.3 小結 183
4.3 jquery.when( deferreds ) 184
4.3.1 實作原理 185
4.3.2 源碼分析 185
4.4 異步隊列在jquery中的應用 187
4.5 總結 188
第5章 資料緩存data 189
5.1 實作原理 189
5.1.1 為dom元素附加資料 189
5.1.2 為javascript對象附加資料 191
5.2 總體結構 192
5.3 jquery.acceptdata( elem ) 193
5.4 jquery.data( elem, name, data, pvt )、jquery._data( elem, name, data, pvt ) 194
5.4.1 如何使用 194
5.4.2 源碼分析 194
5.4.3 jquery._data( elem, name, data ) 199
5.4.4 小結 201
5.5 .data( key,value ) 201
5.5.1 如何使用 201
5.5.2 源碼分析 202
5.5.3 小結 206
5.6 jquery.removedata( elem,name,pvt )、.removedata( key ) 207
5.6.1 如何使用 207
5.6.2 源碼分析 207
5.6.3 小結 212
5.7 .removedata( key ) 213
5.8 jquery.cleandata( elems ) 213
5.8.1 應用場景 213
5.8.2 源碼分析 214
5.8.3 小結 217
5.9 jquery.hasdata( elem ) 217
5.10 總結 218
第6章 隊列queue 219
6.1 如何使用 219
6.1.1 ajax隊列 220
6.1.2 動畫隊列+ ajax隊列 220
6.1.3 基于javascript對象 221
6.2 實作原理 221
6.3 總體結構 222
6.4 jquery.queue( elem,type,data ) 223
6.5 jquery.dequeue( elem,type ) 224
6.6 .queue( type,data ) 227
6.7 .dequeue( type ) 228
6.8 .delay( time,type ) 229
6.9 .clearqueue( type ) 230
6.10 jquery._mark( elem,type )、jquery._unmark( force,elem,type ) 230
6.11 .promise( type,object ) 232
6.11.1 如何使用 232
6.11.2 實作原理 233
6.11.3 源碼分析 233
6.11.4 handlequeuemarkdefer( elem,type,src ) 235
6.12 總結 237
第7章 浏覽器功能測試support 238
7.1 總體結構 238
7.2 dom測試(15項) 241
7.2.1 leadingwhitespace 241
7.2.2 tbody 242
7.2.3 htmlserialize 243
7.2.4 hrefnormalized 245
7.2.5 checkon 246
7.2.6 noclonechecked 248
7.2.7 optselected 250
7.2.8 optdisabled 251
7.2.9 getsetattribute 253
7.2.10 deleteexpando 256
7.2.11 enctype 258
7.2.12 html5clone 259
7.2.13 radiovalue 261
7.2.14 checkclone 263
7.2.15 appendchecked 264
7.3 樣式測試(3項) 266
7.3.1 style 266
7.3.2 opacity 268
7.3.3 cssfloat 272
7.4 盒模型測試(10項) 273
7.4.1 reliablemarginright 273
7.4.2 reliablehiddenoffsets 276
7.4.3 boxmodel 278
7.4.4 inlineblockneedslayout 280
7.4.5 shrinkwrapblocks 282
7.4.6 doesnotaddborder、doesaddborderfortableandcells 285
7.4.7 fixedposition 287
7.4.8 subtractsborderforoverflownotvisible 290
7.4.9 doesnotincludemargininbodyoffset 292
7.5 事件測試(4項) 294
7.5.1 nocloneevent 294
7.5.2 submitbubbles、changebubbles、focusinbubbles 296
7.6 ajax測試(2項) 298
7.6.1 ajax 298
7.6.2 cors 300
7.7 總結 301
第四部分 功能子產品
第8章 屬性操作attributes 306
8.1 總體結構 307
8.2 jquery.attr( elem, name, value, pass ) 308
8.2.1 源碼分析 308
8.2.2 boolhook 311
8.2.3 nodehook 313
8.2.4 jquery.attrhooks 314
8.2.5 小結 319
8.3 .attr( name, value ) 319
8.4 jquery.removeattr( elem, value ) 321
8.4.1 源碼分析 321
8.4.2 小結 322
8.5 .removeattr( name ) 323
8.6 jquery.prop( elem, name, value ) 323
8.6.1 源碼分析 323
8.6.2 jquery.prophooks 325
8.6.3 小結 326
8.7 .prop( name, value ) 327
8.8 .removeprop( name ) 327
8.9 .addclass( classname ) 328
8.9.1 源碼分析 328
8.9.2 小結 330
8.10 .removeclass( [classname] ) 330
8.10.1 源碼分析 331
8.10.2 小結 333
8.11 .toggleclass( [classname][, switch] ) 333
8.11.1 源碼分析 334
8.11.2 小結 336
8.12 .hasclass( selector ) 336
8.12.1 源碼分析 336
8.12.2 小結 337
8.13 .val( [value] ) 338
8.13.1 源碼分析 338
8.13.2 jquery.valhooks 340
8.13.3 小結 343
8.14 總結 344
第9章 事件系統events 346
9.1 總體結構 346
9.2 實作原理 350
9.3 jquery 事件對象 353
9.3.1 構造函數jquery.event( src, props ) 355
9.3.2 原型對象jquery.event.prototype 357
9.3.3 事件屬性修正方法jquery.event.fix( event ) 360
9.4 綁定事件 367
9.4.1 .on( events [, selector] [, data] , handler( eventobject ) ) 367
9.4.2 jquery.event.add( elem, types, handler, data, selector ) 370
9.5 移除事件 379
9.5.1 .off( events [, selector] [, handler( eventobject ) ] ) 379
9.5.2 jquery.event.remove( elem, types, handler, selector, mappedtypes ) 382
9.6 事件響應 388
9.6.1 主監聽函數 388
9.6.2 jquery.event.dispatch( event ) 390
9.7 手動觸發事件 396
9.7.1 .trigger( eventtype [, extraparameters] )、.triggerhandler( eventtype [, extraparameters] ) 396
9.7.2 jquery.event.trigger( event, data, elem, onlyhandlers ) 397
9.8 事件修正和模拟jquery.event.special 406
9.8.1 ready 408
9.8.2 load 408
9.8.3 focus、blur 409
9.8.4 beforeunload 409
9.8.5 mouseenter、mouseleave 410
9.8.6 submit 412
9.8.7 change 413
9.8.8 focusin、focusout 416
9.8.9 jquery.event.simulate( type, elem, event, bubble ) 417
9.9 事件便捷方法 418
9.10 組合方法 419
9.10.1 .toggle( handler( eventobject ), handler( eventobject ) [, handler( eventobject )] ) 419
9.10.2 .hover( handlerin( eventobject ) [, handlerout( eventobject )] ) 421
9.11 ready 事件 421
9.11.1 總體結構 421
9.11.2 .ready( handler ) 424
9.11.3 jquery.bindready() 424
9.11.4 jquery.holdready( hold ) 427
9.11.5 jquery.ready( wait ) 428
9.12 總結 430
第10章 dom周遊traversing 433
10.1 總體結構 434
10.2 周遊函數 435
10.3 工具函數 437
10.3.1 jquery.dir( elem, dir, until ) 437
10.3.2 jquery.nth( cur, result, dir, elem ) 439
10.3.3 jquery.sibling( n, elem ) 440
10.4 模闆函數 441
10.5 總結 443
第11章 dom操作manipulation 444
11.1 總體結構 444
11.2 插入元素 445
11.2.1 核心方法.dommanip( args, table, callback ) 445
11.2.2 .append( content [, content] ) 451
11.2.3 .prepend( content [, content] ) 452
11.2.4 .before( content [, content] ) 452
11.2.5 .after( content [, content] ) 452
11.2.6 .appendto( target )、.prependto( target )、.insertbefore( target )、.insertafter( target ) 453
11.2.7 .html( [value] ) 454
11.2.8 .text( [text] ) 458
11.3 删除元素 459
11.3.1 .remove( selector, keepdata ) 459
11.3.2 .empty() 460
11.3.3 .detach( selector ) 460
11.4 複制元素 461
11.4.1 .clone( dataandevents, deepdataandevents ) 461
11.4.2 jquery.clone( elem, dataandevents, deepdataandevents ) 461
11.4.3 clonefixattributes( src, dest ) 465
11.5 替換元素 467
11.5.1 .replacewith( value ) 467
11.5.2 .replaceall( target ) 469
11.6 包裹元素 469
11.6.1 .wrapall( html ) 469
11.6.2 .wrapinner( html ) 470
11.6.3 .wrap( html ) 471
11.6.4 .unwrap() 471
11.7 總結 472
第12章 樣式操作css 474
12.1 内聯樣式、計算樣式 475
12.1.1 總體結構 475
12.1.2 .css( name, value ) 476
12.1.3 jquery.style( elem, name, value, extra ) 477
12.1.4 jquery.css( elem, name, extra ) 481
12.1.5 curcss( elem, name )、getcomputedstyle( elem, name )、currentstyle( elem, name ) 483
12.1.6 jquery.csshooks 486
12.2 坐标offset 492
12.2.1 總體結構 492
12.2.2 .offset( options ) 493
12.2.3 jquery.offset.setoffset( elem, options, i ) 498
12.2.4 jquery.offset.bodyoffset( body ) 500
12.2.5 .position() 501
12.2.6 .offsetparent() 502
12.2.7 .scrollleft( val )、.scrolltop( val ) 503
12.3 尺寸dimensions 504
12.3.1 總體結構 504
12.3.2 getwh( elem, name, extra ) 505
12.3.3 .innerheight()、.innerwidth() 508
12.3.4 .outerheight( margin )、.outerwidth( margin ) 509
12.3.5 .height( size )、.width( size ) 509
12.3.6 小結 513
12.4 總結 513
第13章 異步請求ajax 516
13.1 總體結構 517
13.2 jquery.ajax( url, options ) 519
13.3 前置過濾器、請求發送器的初始化和執行 540
13.3.1 初始化 540
13.3.2 執行 543
13.4 前置過濾器 545
13.4.1 json、jsonp 545
13.4.2 script 548
13.4.3 小結 549
13.5 請求發送器 549
13.5.1 script 549
13.5.2 xmlhttprequest 552
13.5.3 小結 560
13.6 資料轉換器 561
13.6.1 初始化 561
13.6.2 執行 562
13.6.3 小結 566
13.7 ajax事件 567
13.8 便捷方法 568
13.8.1 jquery.get( url, data, callback, type )、jquery.post( url, data, callback, type ) 569
13.8.2 jquery.getjson( url, data, callback )、jquery.getscript( url, callback ) 569
13.8.3 .load( url, params, callback ) 570
13.9 工具方法 573
13.9.1 .serialize() 573
13.9.2 jquery.param( a, traditional ) 574
13.9.3 .serializearray() 577
13.10 總結 579
第14章 動畫effects 582
14.1 總體結構 583
14.2 動畫入口 586
14.2.1 .animate( prop, speed, easing, callback ) 586
14.2.2 jquery.speed( speed, easing, fn ) 588
14.2.3 doanimation() 590
14.2.4 jquery.fx( elem, options, prop ) 595
14.2.5 jquery.fx.prototype.show() 595
14.2.6 jquery.fx.prototype.hide() 596
14.2.7 小結 596
14.3 動畫執行 597
14.3.1 jquery.fx.prototype.custom( from, to, unit ) 598
14.3.2 jquery.fx.tick() 599
14.3.3 jquery.fx.prototype.step( gotoend ) 600
14.3.4 jquery.easing 604
14.3.5 jquery.fx.prototype.update() 604
14.3.6 jquery.fx.step 605
14.4 停止動畫.stop( type, clearqueue, gotoend ) 606
14.5 便捷方法 609
14.5.1 生成動畫樣式集genfx( type, num ) 609
14.5.2 顯示隐藏.show/hide/toggle() 610
14.5.3 漸顯漸隐.fadein/fadeout/fadeto/fadetoggle() 613
14.5.4 滑入滑出.slidedown/slidup/slidetoggle() 614
14.6 總結 615
第一部分
總 體 架 構
第1章 總體架構
第1章
1.1 設計理念
jquery是一款革命性的javascript庫,秉承着“以用為本”的設計理念,倡導“寫更少的代碼,做更多的事”(write less, do more),極大地提升了javascript開發體驗。
jquery的核心特性可以總結為:
相容主流浏覽器,支援ie 6.0+、chrome、firefox 3.6+、safari 5.0+、opera等。
具有獨特的鍊式文法和短小清晰的多功能接口。
具有高效靈活的css選擇器,并且可對css選擇器進行擴充。
擁有便捷的插件擴充機制和豐富的插件。
1.2 總體架構
jquery的子產品可以分為3部分:入口子產品、底層支援子產品和功能子產品,如圖1-1所示,圖中還展示了子產品之間的主要依賴關系。
來看看圖1-1中各個子產品的功能和依賴關系。
在構造jquery對象子產品中,如果在調用構造函數jquery()建立jquery對象時傳入了選擇器表達式,則會調用選擇器sizzle周遊文檔,查找與之比對的dom元素,并建立一個包含了這些dom元素引用的jquery對象。
選擇器sizzle是一款純javascript實作的css選擇器引擎,用于查找與選擇器表達式比對的元素集合。
工具方法子產品提供了一些程式設計輔助方法,用于簡化對jquery對象、dom元素、數組、對象、字元串等的操作,例如,jquery.each()、.each()、jquery.map()、.map()等,其他所有的子產品都會用到工具方法子產品。
圖1-1 jquery的子產品分類和主要依賴關系
浏覽器功能測試子產品提供了針對不同浏覽器功能和bug的測試結果,其他子產品則基于這些測試結果來解決浏覽器之間的相容性問題。
在底層支援子產品中,回調函數清單子產品用于增強對回調函數的管理,支援添加、移除、觸發、鎖定、禁用回調函數等功能;異步隊列子產品用于解耦異步任務和回調函數,它在回調函數清單的基礎上為回調函數增加了狀态,并提供了多個回調函數清單,支援傳播任意同步或異步回調函數的成功或失敗狀态;資料緩存子產品用于為dom元素和javascript對象附加任意類型的資料;隊列子產品用于管理一組函數,支援函數的入隊和出隊操作,并確定函數按順序執行,它基于資料緩存子產品實作。
在功能子產品中,事件系統提供了統一的事件綁定、響應、手動觸發和移除機制,它并沒有将事件直接綁定到dom元素上,而是基于資料緩存子產品來管理事件;ajax子產品允許從伺服器上加載資料,而不用重新整理頁面,它基于異步隊列子產品來管理和觸發回調函數;動畫子產品用于向網頁中添加動畫效果,它基于隊列子產品來管理和執行動畫函數;屬性操作子產品用于對html屬性和dom屬性進行讀取、設定和移除操作;dom周遊子產品用于在dom樹中周遊父元素、子元素和兄弟元素;dom操作子產品用于插入、移除、複制和替換dom元素;樣式操作子產品用于擷取計算樣式或設定内聯樣式;坐标子產品用于讀取或設定dom元素的文檔坐标;尺寸子產品用于擷取dom元素的高度和寬度。
下面來看看jquery源碼(jquery-1.7.1.js)的總體結構,如代碼清單1-1所示,其中展示了各個子產品在源碼中的位置。
代碼清單1-1 jquery源碼(jquery-1.7.1.js)的總體結構
(function( window, undefined ) {
// 構造jquery對象
var jquery = (function() {
var jquery = function( selector, context ) {
return new jquery.fn.init( selector, context, rootjquery );
}
return jquery;
})();
// 工具方法 utilities
// 回調函數清單 callbacks object
// 異步隊列 deferred object
// 浏覽器功能測試 support
// 資料緩存 data
// 隊列 queue
// 屬性操作 attributes
// 事件系統 events
// 選擇器 sizzle
// dom 周遊 traversing
// dom 操作 manipulation
// 樣式操作 css(計算樣式、内聯樣式)
// 異步請求 ajax
// 動畫 effects
// 坐标 offset、尺寸 dimensions
window.jquery = window.$ = jquery;
})(window);
從代碼清單1-1可以看出,jquery的源碼結構還是相當清晰和有條理的,并不像源碼那般晦澀。
1.3 自調用匿名函數
從代碼清單1-1中還可以看到,jquery的所有代碼都被包裹在了一個立即執行的匿名函數表達式中,這種代碼結構稱為“自調用匿名函數”。當浏覽器加載完jquery檔案後,自調用匿名函數會立即開始執行,初始化jquery的各個子產品。相關代碼如下所示:
var jquery = ...
// ...
上面這段代碼涉及一些javascript基礎知識和編碼習慣,下面以提問的方式來逐一分析。
1)為什麼要建立這樣一個自調用匿名函數?
通過建立一個自調用匿名函數,建立了一個特殊的函數作用域,該作用域中的代碼不會和已有的同名函數、方法和變量以及第三方庫沖突。由于jquery會被應用在成千上萬的javascript程式中,是以必須確定jquery的代碼不會受到其他代碼的幹擾,并且jquery不能破壞和污染全局變量以至于影響到其他代碼。這一點是任何一個javascript庫和架構所必須具備的功能。
注意,在這個自調用匿名函數的最後,通過手動把變量jquery添加到window對象上,明确地使變量jquery成為公開的全局變量,而其他的部分将是私有的。
另外,自調用匿名函數還可以有兩種等價的寫法,如下所示(注意加了底紋的圓括号的位置):
// 寫法1(常見寫法,也是 jquery 所采用的)
( function() {
} )();
// 寫法2
}() );
// 寫法3
!function() {
}();
2)為什麼要為自調用匿名函數設定參數window,并傳入window對象?
通過傳入window對象,可以使window對象變為局部變量(即把函數參數作為局部變量使用),這樣當在jquery代碼塊中通路window對象時,不需要将作用域鍊回退到頂層作用域,進而可以更快地通路window對象,這是原因之一;另外,将window對象作為參數傳入,可以在壓縮代碼時進行優化,在壓縮檔案jquery-1.7.1.min.js中可以看到下面的代碼:
(function(a,b){ ... })(window);
// 參數 window 被壓縮為 a,參數 undefined 被壓縮為 b
3)什麼要為自調用匿名函數設定參數undefined?
特殊值undefined是window對象的一個屬性,例如,執行下面的代碼将會彈出true:
alert( "undefined" in window ); // true
通過把參數undefined作為局部變量使用,但是又不傳入任何值,可以縮短查找undefined時的作用域鍊,并且可以在壓縮代碼時進行優化,如前面代碼所示,參數undefined會被壓縮為b。
另外,更重要的原因是,通過這種方式可以確定參數undefined的值是undefined,因為undefiend有可能會被重寫為新的值。可以用下面的代碼來嘗試修改undefined的值,在各浏覽器中的測試結果見表1-1。
undefined = "now it's defined";
alert( undefined );
表1-1 在浏覽器中嘗試修改 undefined 的值
浏 覽 器 測 試 結 果 結 論
ie 6.0、ie 7.0、ie 8.0 now it's defined 可以改變
ie 9.0、ie 10.0 undefined 不能改變
chrome 16.0.912.77 now it's defined 可以改變
chrome 17.0.963.56 undefined 不能改變
firefox 3.6.28 now it's defined 可以改變
firefox 4.0 undefined 不能改變
safari 4.0.2 now it's defined 可以改變
safari 4.0.4 undefined 不能改變
opera 11.52 now it's defined 可以改變
opera 11.60 undefined 不能改變
4)注意到自調用匿名函數最後的分号(;)了嗎?
通常在javascript中,如果語句分别放置在不同的行中,則分号(;)是可選的,但是對于自調用匿名函數來說,在之前或之後省略分号都可能會引起文法錯誤。例如,執行下面的兩個例子,就會抛出異常。
例1 在下面的代碼中,如果自調用匿名函數的前一行末尾沒有加分号,則自調用匿名函數的第一對括号會被當作是函數調用。
var n = 1
( function(){} )()
// typeerror: number is not a function
例2 在下面的代碼中,如果未在第一個自調用匿名函數的末尾加分号,則下一行自調用匿名函數的第一對括号會被當作是函數調用。
// typeerror: undefined is not a function
是以,在使用自調用匿名函數時,最好不要省略自調用匿名函數之前和之後的分号。
1.4 總結
在本章的前半部分,對jquery的總體架構進行了梳理,并對各個子產品的分類、功能和主要依賴關系做了簡要介紹。通過這些介紹,讀者已經對jquery的代碼有了整體上的認識。
在後半部分,則以提問的方式對包裹jquery代碼的自調用匿名函數進行了分析,掃除了讀者閱讀jquery源碼的第一道障礙。