函數是一個對象:它建立了範圍
這是因為現在你已經把isdoingwork這個變量建立在了一個函數裡面 -- 也就是我們們的匿名 iife 中 -- 而如此這個變量就隻能通過這個函數才能通路到. 有趣的是javascript中的所有函數都是第一類對象. 那很簡明的意味着函數是一個對象,它可能通過一個變量被通路到. 或者說,另外一種描述的方式是你存儲了指向 函數的一個引用,并在稍後的某個時間擷取其變量.
在我們第一個示例中,我們的問題是并沒有儲存一個指向我們匿名函數的引用,是以我們永遠也不能再擷取到isdoingwork這個值。這就是我們下一個示例要改進的地方.
函數是一個對象 : 使用this
因為每一個函數都是一個對象,是以每個函數都會有一個this變量,這個變量向開發者提供了指向目前對象的引用. 為了提供在從外部大我們的函數及其範圍的通路,我們可以傳回這個this變量 -- 而它将會提供一個指向目前對象的引用.
然後,除非我們将這個私有的isdoingwork變量添加到函數引用(this)上,我們也不能夠引用這個變量。為此我們要對之前的示例做一下輕微的改動。它看起來會像下面這樣:
你可以看到第一行我們加入了一個新的全局變量thing,它包含了從匿名函數傳回的值。從示例代碼的開頭跳到第三行,你可以看到我們傳回了this變量。那就意味着我們傳回了一個指向匿名函數的引用.
在第二行我們也已經将isdoingwork加入了this引用中,那樣我們就可以使用文法thing.isdoingwork來從外部引用到這個值了.
自己動手看看
為了看看的運作,你可以做下面這幾步:
1.下載下傳本文的示例代碼.
2.在你的浏覽器中打開 modulepattern4.htm.
3.打開浏覽器開發工具 -- f12(chrome, ie) 或者 ctrl-shift-i (opera) -- (那樣你就可以看到控制台了)
4.你将會看到isdoingwork的值會輸出到控制台,就像最開始那個示例中你看到的那樣.
5.不過,現在你得輸入thing.isdoingwork才能或者這個值.
子產品模式總結
在最後這個示例中,變量值被成功的封裝了,而其他的javascript庫則可以明确的引用thing對象來擷取這個值. 好像不大可能,而這幫助了我們保持全局命名空間的幹淨,并且在看起看來是更好的代碼組織形式. 這也使得我們代碼的維護更容易.
最終,我們用上了 angularjs
因為使用子產品模式是一個最佳實踐,angularjs的開發者就将一個子產品系統建構到了庫中.
plunker 代碼
而我們在這裡展示出代碼,那樣我們就可以更友善的談論它了.
首先,讓我們看看這個 html.
angular 指令 : ng-app
angular 所定義和使用的東西叫做指令。這些指令基本上就是由angular定義屬性,而angularjs編譯器(angular的javascript)會将它們轉換成其他的東西.
我們應用了ng-app指令,為我們的angular應用定義了一個名稱,叫做mainapp.
mainapp 就是我們稍後會看到的子產品模式的起點.
被引入的腳本 : 每個都是一個子產品
現在,請注意有三個腳本被引入到了這個html中.
第一個是必須的angularjs庫.
而其他兩個則是作為子產品被實作的angular控制器.
它們被作為子產品實作以保持代碼彼此,還有從這個應用上看,都是獨立的.
angularjs : 建立 score
在往下看,你将會看到兩個以如下代碼開頭的div:
這是在為div的每一個都設定上ng-controller. 這些div中的每一個都有其各自的範圍. 第一個控制器的名字叫做 mainctrl,第二個叫做 secondctrl.
angularjs 編譯器會在你提供(引入)的代碼中用這兩個名稱查找對應的函數.
如果angularjs編譯器沒有找個這兩個名稱對應的函數,它就會抛出一個錯誤.
mainctrl.js : 第一個控制器
讓我們來看看mainctrl.js檔案裡面有些啥東西.
你可以在plunker頁面的左側點選它在plunker中将其打開.
當你打開了它,你将會看到一些看上去很熟悉的代碼。好吧,你至少會看出來它們都是被包在一個iife中的.
那是因為我們需要這些代碼在檔案mainctrl.js被加載時就運作.
現在,請注意在這個iife中的第一行代碼.
這行代碼是angular将一個子產品添加到其命名空間的方式. 在這裡,我們添加了一個将用來展示我們應用程式的子產品. 這是應用程式的子產品,而我們已經将其命名為 itmainapp, 它跟html頁面上ng-app所指定的值是一樣的.
我們也建立了一個叫做app的(隻在iife本地可見的)本地變量,以便我們将可以在這個函數内部用來再次添加一個控制器.
奇怪的 angular 文法
請你也要再仔細看看第一行。你會注意到我們是首次建立mainapp子產品,而如果是首次,則我們必須提供以字元串數組的形式提供其可能需要的任何依賴(,表示出依賴庫的名稱). 不過,在這裡對于這個簡單的示例而言,我們不需要任何的依賴。但angular仍然需要我們傳入一個空的數組,以便它知曉我們正在建立新的子產品,而不是去試圖加載一個已經被建立好了的子產品.
提示: 你将會看到我們會在secondctrl.js裡加載mainapp子產品,而上面所提的數組将會有更多的作用.
我們一把mainapp建立好,就需要向其添加我們的控制器. 這些就是angular預期我們在html(的div中)加入的控制器.
将控制器添加到app子產品
添加控制器的代碼看起來像下面這樣:
為了添加我們的控制器函數,我們向app.controller()函數提供了一個控制器名稱和一個函數. 在此處我們提供了一個匿名函數.
是以,我們的控制器主體代碼就是下面這幾行了:
這裡,當我們的控制器運作時,會向控制台輸出一行. 然後,我們将this變量重命名為vt(友善起見,就叫他虛拟的this) ,而後我天為其添加了一個name屬性和一個叫做allthings 的字元串數組.
控制器和封裝
那就是當控制器被angular調用時會運作的代碼. 那個控制器會在檔案被加載時運作起來,也就是一開始html被加載的時候. 這意味着控制器會被加載到app子產品中,而這些屬性會被添加到控制器對象(函數)中。因為我們想this變量添加了屬性,我們就可以在稍後擷取這些屬性,但它們是被封裝了起來的,是以它們不可以被每個人随意的更改.
現在,讓我們跳到html中控制器被引用和使用的地方.
第一個div
這是我們的mainctrl控制器被引用和使用的第一個div。它看起來就像下面這樣:
這個div輸出我們的web頁面的如下部分,看起來就是接下來這張圖檔上所展示的那樣.
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnL0UGM1ITO3QGNxYTNklDM3MjYhNGZyU2MwMGZiRjMiZWOwImZkVTOi9CXt92Yu4GZjlGbh5SZslmZxl3Lc9CX6MHc0RHaiojIsJye.png)