天天看點

jQuery技術内幕:深入解析jQuery架構設計與實作原理1

jQuery技術内幕:深入解析jQuery架構設計與實作原理1

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源碼的第一道障礙。

繼續閱讀