天天看點

Angularjs 源碼分析2

說起這個$get屬性,是每個系統provider都有的,主要是先儲存要執行個體化的函數體,等待<code>instanceinjector.invoke</code>的時候來調用,因為$get的代碼比較多,是以先上要講的那部分,大家可以注意到了,在$get上面有一個<code>digestttl</code>方法

這個是用來修改系統預設的<code>dirty check</code>次數的,預設是10次,通過在<code>config</code>裡引用<code>rootscope</code>provider,可以調用這個方法傳遞不同的值來修改<code>ttl(short for time to live)</code>

下面來看下$get中的scope構造函數

可以看到在構造函數裡定義了很多屬性,我們來一一說明一下

$id, 通過nextuid方法來生成一個唯一的辨別

$$phase, 這是一個狀态辨別,一般在<code>dirty check</code>時用到,表明現在在哪個階段

$parent, 代表自己的上級scope屬性

$$watchers, 儲存scope變量目前所有的監控資料,是一個數組

$$nextsibling, 下一個兄弟scope屬性

$$prevsibling, 前一個兄弟scope屬性

$$childhead, 第一個子級scope屬性

$$childtail, 最後一個子級scope屬性

$$destroyed, 表示是否被銷毀

$$asyncqueue, 代表異步操作的數組

$$postdigestqueue, 代表一個在<code>dirty check</code>之後執行的數組

$$listeners, 代表scope變量目前所有的監聽資料,是一個數組

$$listenercount, 暫無

$$isolatebindings, 暫無

通過這段代碼,可以看出,系統預設會建立根作用域,并作為<code>$rootscope</code>provider執行個體傳回.

建立子級作用域是通過<code>$new</code>方法,我們來看看.

通過分析上面的代碼,可以得出

isolate辨別來建立獨立作用域,這個在建立指令,并且scope屬性定義的情況下,會觸發這種情況,還有幾種别的特殊情況,假如是獨立作用域的話,會多一個$root屬性,這個預設是指向rootscope的

如果不是獨立的作用域,則會生成一個内部的構造函數,把此構造函數的prototype指向目前scope執行個體

通用的操作就是,設定目前作用域的$$childtail,$$childtail.$$nextsibling,$$childhead,this.$$childtail為生成的子級作用域;設定子級域的$parent為目前作用域,$$prevsibling為目前作用域最後一個子級作用域

說完了建立作用域,再來說說<code>$watch</code>函數,這個比較關鍵

<code>$watch</code>函數有三個參數,第一個是監控參數,可以是字元串或者函數,第二個是監聽函數,第三個是代表是否深度監聽,注意看這個代碼

這個<code>compiletofn</code>函數其實是調用<code>$parse</code>執行個體來分析監控參數,然後傳回一個函數,這個會在<code>dirty check</code>裡用到,用來擷取監控表達式的值,這個<code>$parse</code>provider也是angularjs中用的比較多的,下面來重點的說下這個provider

<code>$parse</code>的代碼比較長,在源碼檔案夾中的ng目錄裡,<code>parse.js</code>裡就是$parse的全部代碼,當你了解完<code>parse</code>的核心之後,這部份代碼其實可以獨立出來,做成自己的電腦程式也是可以的,因為它的核心就是解析字元串,而且預設支援四則運算,運算符号的優先級處理,隻是額外的增加了對變量的支援以及過濾器的支援,想想,把這塊代碼放在模闆引擎裡也是可以的,說多了,讓我們來一步一步的分析<code>parse</code>代碼吧.

記住,不管是哪個provider,先看它的<code>$get</code>屬性,是以我們先來看看<code>$parse</code>的<code>$get</code>吧

可以看出,假如解析的是函數,則直接傳回,是字元串的話,則需要進行<code>parser.parse</code>方法,這裡重點說下這個

通過閱讀<code>parse.js</code>檔案,你會發現,這裡有兩個關鍵類

lexer, 負責解析字元串,然後生成<code>token</code>,有點類似編譯原理中的詞法分析器

parser, 負責對lexer生成的<code>token</code>,生成執行表達式,其實就是傳回一個執行函數

看這裡

第一句就是建立一個lexer執行個體,第二句是把lexer執行個體傳給parser構造函數,然後生成parser執行個體,最後一句是調用<code>parser.parse</code>生成執行表達式,實質是一個函數

現在轉到<code>parser.parse</code>裡去

視線移到這句<code>this.tokens = this.lexer.lex(text)</code>,然後來看看<code>lex</code>方法

這裡我們假如傳進的字元串是<code>1+2</code>,通常我們分析源碼的時候,碰到代碼複雜的地方,我們可以簡單化處理,因為邏輯都一樣,隻是情況不一樣罷了.

上面的代碼主要就是分析傳入到<code>lex</code>内的字元串,以一個<code>while</code>loop開始,然後依次檢查目前字元是否是數字,是否是變量辨別等,假如是數字的話,則轉到 <code>readnumber</code>方法,這裡以<code>1+2</code>為例,目前<code>ch</code>是<code>1</code>,然後跳到<code>readnumber</code>方法

上面的代碼就是檢查從目前<code>index</code>開始的整個數字,包括帶小數點的情況,檢查完畢之後跳出loop,目前<code>index</code>向前進一個,以待以後檢查後續字元串,最後儲存到lex執行個體的<code>token</code>數組中,這裡的<code>fn</code>屬性就是以後執行時用到的,這裡的<code>return number</code>是利用了js的閉包特性,<code>number</code>其實就是檢查時外層的<code>number</code>變量值.以<code>1+2</code>為例,這時<code>index</code>應該停在<code>+</code>這裡,在<code>lex</code>的<code>while loop</code>中,<code>+</code>檢查會跳到最後一個<code>else</code>裡,這裡有一個對象比較關鍵,<code>operators</code>,它儲存着所有運算符所對應的動作,比如這裡的<code>+</code>,對應的動作是

大家注意了,這裡有4個參數,可以先透露一下,第一個是傳的是目前上下文對象,比喻目前<code>scope</code>執行個體,這個是為了擷取字元串中的變量值,第二個參數是本地變量,是傳遞給函數當入參用的,基本用不到,最後兩個參是關鍵,<code>+</code>是二進制運算符,是以a代表左側運算值,b代表右側運算值.

最後解析完<code>+</code>之後,<code>index</code>停在了<code>2</code>的位置,跟<code>1</code>一樣,也是傳回一個<code>token</code>,<code>fn</code>屬性也是一個傳回目前數字的函數.

當解析完整個<code>1+2</code>字元串後,<code>lex</code>傳回的是<code>token</code>數組,這個即可傳遞給<code>parse</code>來處理,來看看

預設json是<code>false</code>,是以會跳到<code>this.statements()</code>,這裡将會生成執行語句.

代碼以一個無限<code>loop</code>的<code>while</code>開始,語句分析的時候是有運算符優先級的,預設的順序是,這裡以函數名為排序

filterchain&lt;expression&lt;assignment&lt;ternary&lt;logicalor&lt;logicaland&lt;equality&lt;relational&lt;additive&lt;multiplicative&lt;unary&lt;primary

中文翻譯下就是這樣的

過濾函數&lt;一般表達式&lt;指派語句&lt;三元運算&lt;邏輯or&lt;邏輯and&lt;比較運算&lt;關系運算&lt;加減法運算&lt;乘法運算&lt;一進制運算,最後則預設取第一個<code>token</code>的<code>fn</code>屬性

這裡以<code>1+2</code>的<code>token</code>為例,這裡會用到<code>parse</code>的<code>expect</code>方法,<code>expect</code>會用到<code>peek</code>方法

<code>expect</code>方法傳空就是預設從<code>token</code>數組中彈出第一個<code>token</code>,數組數量減1

<code>1+2</code>的執行語句最後會定位到加法運算那裡<code>additive</code>

最後傳回一個二進制操作的函數<code>binaryfn</code>

這個函數參數裡的<code>left</code>,<code>right</code>對應的'1','2'兩個<code>token</code>的<code>fn</code>屬性,即是

<code>fn</code>函數對應<code>additive</code>方法中<code>+</code>号對應<code>token</code>的<code>fn</code>

最後生成執行表達式函數,也就是<code>filterchain</code>傳回的<code>left</code>值,被<code>push</code>到<code>statements</code>方法中的<code>statements</code>數組中,仔細看<code>statements</code>方法的傳回值,假如表達式數組長度為1,則傳回第一個執行表達式,否則傳回一個包裝的函數,裡面是一個<code>loop</code>,不斷的執行表達式,隻傳回最後一個表達式的值

好了,說完了生成執行表達式,其實<code>parse</code>的任務已經完成了,現在隻需要把這個作為<code>parse</code>provider的傳回值了.

今天先說到這裡了,下次有空接着分析<code>rootscope</code>後續的方法.

繼續閱讀