下載 modulePattern.zip - 所有的 4 個 HTML 文件 以及 panacea.js - 1.6 KB
------------------------------------------分割線------------------------------------------
免費下載地址在 http://linux.linuxidc.com/
用戶名與密碼都是www.linuxidc.com
具體下載目錄在 /2014年資料/8月/8日/AngularJS —— 使用模塊組織你的代碼
下載方法見 http://www.linuxidc.com/Linux/2013-07/87684.htm
------------------------------------------分割線------------------------------------------
AngularJS 的庫裡面有很多東西,但本文中我只想專注於小的,針對特定主題的庫,我相信通過它們能對Angular有一個較好的介紹. 理解這篇文章並不需要你有任何Angular相關的,甚至是JavaScript的經驗。希望你能從本文中看到一些使用Angular的好處,並樂於動手嘗試。
我使用Angular有一段時間了,而在學習Angular的時候,我也喜歡構建一些樣例,所以當我一開始深入進去的時候,對於模塊或者JavaScript的設計模式,我也沒有多想,那樣對保持代碼組織和條理性有幫助. 那就是所有的重點:保持代碼的組織和條理性. 因此,現在我回過頭來,創建了這個極其小巧的樣例,以展示使用模塊可以有多簡單. 一路走來,我希望它能夠成為一篇好的對Angular的介紹.
大多數時候人們都會嘗試去在讀者知道模式是啥概念之前就開始闡述一個模式,而這基本上誤導了每一個人. 這裡要努力使得本文盡量簡單,讓我們首先來看一看這個問題吧。哪個問題呢?就是有關默認會在全局內存空間被創建的所有東西的Javascript的問題.
下面就是我所說的意思.
設想你的HTML中有下面這樣一段腳本.
<script>
var isDoingWork =
false
;
</script>
范圍?
你清楚這個變量的范圍麼?
是的,它是全局的。這個布爾值實際上被添加到了浏覽器的全局窗口對象中.
這裡你可以看到它在Action中是怎樣的.
下載本文的代碼樣例.
在你的浏覽器中打開 modulePattern.htm .
打開浏覽器開發工具 -- F12(Chrome, IE) or Ctrl-Shift-I (Opera) -- (那樣就可以看見控制台了)
在浏覽器工具控制台下,輸入: isDoingWork,然後回車<ENTER>
你會看到輸出的值為false.
現在輸入 : isDoingWork = true,然後回車<ENTER>
如此下載的值就為true了. 你已經改變了這個值.
你可以看到這個值已經通過輸入doingwindow.isDoingWork = true然後回車<ENTER>,被添加到了全局窗口對象之中.
這可能會造成一些名字沖突,也會導致一些嚴重的bug. 這也許對你而言有點杞人憂天了,是不? 但是請設想你是決定要去實現某一個新的JS庫,它每分每秒都可以被創建出來. 假設你發現了這個叫做 Panacea.js 的很棒的庫,它將解決你所有的問題.
因此你向下面這樣在你的頁面中引用了它:
<script src=
"panacea.js"
></script>
如此簡單,你就已經解決之前你遇到的所有問題. 然而,因為它是一個龐大的庫,而你只想要解決方法,卻不回去深挖這個龐大(幾千行代碼)源文件裡的每一行代碼. 而深埋在 Panacea.js 裡面某個角落的確實下面這樣的代碼:
var isDoingWork =
false
;
setInterval(function(){isDoingWork = !isDoingWork;},
3000
);
這代碼真是酷,你知道嗎?
每個3秒,它都會將這個布爾值設置成相對的值。啊!
如果你想要自己動手驗證下這個東西,你可以做下面這幾步:
下載本文的樣例代碼.
在你的浏覽器中打開 modulePattern2.htm .
打開浏覽器開發工具 -- F12(Chrome, IE) 或者 Ctrl-Shift-I (Opera) -- (這樣你就可以看到控制台了)
在浏覽器開發工具的控制台下,輸入 : isDoingWork 然後回車<ENTER>
將第4步多重復幾次,你將會發現isDoingWork的值會每個大約3秒鐘變化一次.
那這是不是很棒呢?
帶你走近AngularJS系列:
如何在 AngularJS 中對控制器進行單元測試 http://www.linuxidc.com/Linux/2013-12/94166.htm
在 AngularJS 應用中通過 JSON 文件來設置狀態 http://www.linuxidc.com/Linux/2014-07/104083.htm
AngularJS 之 Factory vs Service vs Provider http://www.linuxidc.com/Linux/2014-05/101475.htm
AngularJS —— 使用 ngResource、RESTful APIs 和 Spring MVC 框架提交數據 http://www.linuxidc.com/Linux/2014-07/104402.htm
我需要為此做出解釋,為了要向你展示為什麼 JavaScript 的模塊模式是很有用的. 我得想你展示 JavaScript 的模塊模式,那樣我就可以告訴你它是如何在AngularJS中被使用或實現的了.
如此,實際就是,模塊模式基本上就是封裝了. 封裝聽起來很熟悉,如果你有點面向對象編程經歷的話 -- 而我也希望你能有點這個經驗. 封裝是面向對象編程的三原則之一。封裝的另外一個說法就是數據隱藏。在經典的面向對象編程中——它不同於JavaScript所依賴的原型化OOP -- 數據隱藏是構建一個類模板的內在組成部分.
例如在 C#中, Animal 類的封裝 -- 隱藏數據 -- 特定的值被關聯到Animal對象. 那樣,如果某人決定變更那些值,他或他必須明確的通過初始化一個Animal對象並設置這個對象的值來達到目的. 在JavaScript中,我們則可以隨意的在全局窗口對象中設置值.
public
class
Animal
{
// constructor allows user to set the commonName
public
animal(string name)
{
this
.commonName = name;
}
// making commonName private hides (encapsulates) it in the class
private
string commonName;
//explicitly exposing the value for reading only
public
string CommonName get {
return
this
.commonName }
}
在JavaScript中,模塊已經被創建用來模擬這種封裝行為了,如此我們就不會去將我們的變量組織到一個全局的命名空間中,並造成了隱藏很深的難以被發現和修復的問題.
現在你知道為什麼了,讓我們來看看如何會是這樣的.
看上去就好像每次我們向前推進一步,我們都要走點旁門左道. 因為要獲得能讓我們創建模塊模式的JavaScript語法,我們就得去了解一種叫做函數被立即調用的表達式語法,也叫做IIFE ( IIFE 發音是 "iffy").
最基礎的 IIFE 看起來像這樣:
(function(){
// lines
// of
// code
}());
如果你從來沒有看到過像這樣的東西,那你就有點說不過去了.
首先,這個名稱的第一部分叫做立即被調用的原因是,一般包含這個特殊函數的源文件被加載好了,那麼包含在這個函數中的代碼就會運行.
你可以看到這個語法的最中心是一個函數。看一下這個代碼塊,我已經將代碼分段並將一些行標上了號,如此我們就可以探討它了.
(
// 1.
function()
//2.
{
// 3.
// 一行一行
// 的
// 代碼
}()
// 4.
);
// 5.
首先,看看上面腳本的第2行。這一行通常看來就是一個匿名(也就是沒有命名)的函數聲明. 而後,第3一直到第4則是這個函數的主題部分。最後,第4行最後以一對括弧結束,這對 括弧會告訴JavaScript解釋器去調用這個函數。最終, 所有這些都會被包在一個不歸屬任何部分的括弧(第1和第5行)中, 而這對括弧會告訴解釋器要調用這個外部的匿名函數,它包含了我們所定義的函數.
IIFE 可以帶上參數
這段奇怪的語法會在帶上參數之後,看起來會更加的奇怪. 它看起來會像是下面這樣
(function(thing1, thing2){
// lines
// of
// code
}(
"in string"
,
382
));
現在,你可以看到這個函數可以帶上兩個會被內部的函數引用的thing1, thing2參數. 被傳入值,在示例中是 "in string" 和 382.
現在我們理解了IIFE語法,讓我們來創建另外一個代碼示例,我們將運行這段代碼來看看封裝是如何運作的.
(function(){
var isDoingWork =
false
;
console.log(
"isDoingWork value : "
+ isDoingWork);
}());
為了看看是怎麼運行的,你可以做下面這幾步:
下載本文的源代碼.
在你的浏覽器中打開 modulePattern3.htm.
打開浏覽器的開發工具 -- F12(Chrome, IE) 或者 Ctrl-Shift-I (Opera) -- (這樣你就可以看到控制台了)
你可以看到很像下面這樣圖片中所展示出來的東西
當方法被調用時 -- 這會在代碼被JavaScript解釋器加載支護立即發生 -- 而後函數會創建 isDoingWork 變量,並調用console.log()來在控制台輸出這個變量的值.
現在,讓我們使用開發工具中的控制台來試試我們之前所嘗試過的步驟:
輸入: isDoingWork然後回車<ENTER>
當你這樣做了之後,你將會看到 浏覽器不再相信isDoingWork這個值被定義過。即使是你嘗試從全局窗口對象中獲取這個值, 浏覽器也不認為 isDoingWork 這個值在此對象中被定義了. 你所看到的錯誤消息看起來會像接下來這張圖片中所展示的這樣.
函數是一個對象:它創建了范圍
這是因為現在你已經把isDoingWork這個變量創建在了一個函數裡面 -- 也就是我們們的匿名 IIFE 中 -- 而如此這個變量就只能通過這個函數才能訪問到. 有趣的是Javascript中的所有函數都是第一類對象. 那很簡明的意味著函數是一個對象,它可能通過一個變量被訪問到. 或者說,另外一種描述的方式是你存儲了指向 函數的一個引用,並在稍後的某個時間獲取其變量.
在我們第一個示例中,我們的問題是並沒有保存一個指向我們匿名函數的引用,所以我們永遠也不能再獲取到isDoingWork這個值。這就是我們下一個示例要改進的地方.
因為每一個函數都是一個對象,所以每個函數都會有一個this變量,這個變量向開發者提供了指向當前對象的引用. 為了提供在從外部大我們的函數及其范圍的訪問,我們可以返回這個this變量 -- 而它將會提供一個指向當前對象的引用.
然後,除非我們將這個私有的isDoingWork變量添加到函數引用(this)上,我們也不能夠引用這個變量。為此我們要對之前的示例做一下輕微的改動。它看起來會像下面這樣:
thing = (function(){
// 1.
this
.isDoingWork =
false
;
// 2.
console.log(
"isDoingWork value : "
+ isDoingWork);
return
this
;
// 3.
}());
你可以看到第一行我們加入了一個新的全局變量thing,它包含了從匿名函數返回的值。從示例代碼的開頭跳到第三行,你可以看到我們返回了this變量。那就意味著我們返回了一個指向匿名函數的引用.
在第二行我們也已經將isDoingWork加入了this引用中,那樣我們就可以使用語法thing.isDoingWork來從外部引用到這個值了.
為了看看的運行,你可以做下面這幾步:
下載本文的示例代碼.
在你的浏覽器中打開 modulePattern4.htm.
打開浏覽器開發工具 -- F12(Chrome, IE) 或者 Ctrl-Shift-I (Opera) -- (那樣你就可以看到控制台了)
你將會看到isDoingWork的值會輸出到控制台,就像最開始那個示例中你看到的那樣.
不過,現在你得輸入thing.isDoingWork才能或者這個值.
在最後這個示例中,變量值被成功的封裝了,而其他的JavaScript庫則可以明確的引用thing對象來獲取這個值. 好像不大可能,而這幫助了我們保持全局命名空間的干淨,並且在看起看來是更好的代碼組織形式. 這也使得我們代碼的維護更容易.
因為使用模塊模式是一個最佳實踐,AngularJS的開發者就將一個模塊系統構建到了庫中.
更多詳情見請繼續閱讀下一頁的精彩內容: http://www.linuxidc.com/Linux/2014-08/105265p2.htm