文章目錄
第一部分 NODE JS 是什嗎?
選摘自:Michael Abernethy, Node.js 究竟是什嗎?
Node 旨在解決什麼問題?
Node 公開宣稱的目標是 “旨在提供一種簡單的構建可伸縮網路程式的方法”。當前的伺服器程式有什麼問題?整個 Web 應用程式架構(包括流量、處理器速度和記憶體速度)中的瓶頸是:伺服器能夠處理的並發串連的最大數量。
Node 解決這個問題的方法是:更改串連到伺服器的方式。每個串連發射一個在 Node 引擎的進程中啟動並執行事件,而不是為每個串連產生一個新的 OS 線程(並為其分配一些配套記憶體)。Node 聲稱它絕不會死結,因為它根本不允許使用鎖,它不會直接阻塞 I/O 調用。Node 還宣稱,運行它的伺服器能支援數萬個並發串連。
Node 如何工作?
Node 本身運行 V8 JavaScript。伺服器上的 JavaScript。
V8 JavaScript 引擎是 Google 用於其 Chrome 瀏覽器的底層 JavaScript 引擎。很少有人考慮 JavaScript 在客戶機上實際做了些什嗎?實際上,JavaScript 引擎負責解釋並執行代碼。Google 使用 V8 建立了一個用 C++ 編寫的超快解譯器,該解譯器擁有另一個獨特特徵;您可以下載該引擎並將其嵌入任何 應用程式。V8 JavaScript 引擎並不僅限於在一個瀏覽器中運行。因此,Node 實際上會使用 Google 編寫的 V8 JavaScript 引擎,並將其重建為可在伺服器上使用。
事件驅動編程
許多程式員接受的教育使他們認為,物件導向編程是完美的編程設計,這使得他們對其他編程方法不屑一顧。Node 使用了一個所謂的事件驅動編程模型。
清單 1. 用戶端上使用 jQuery 的事件驅動編程
// jQuery code on the client-side showing how Event-Driven programming works// When a button is pressed, an Event occurs - deal with it// directly right here in an anonymous function, where all the// necessary variables are present and can be referenced directly$("#myButton").click(function(){ if ($("#myTextField").val() != $(this).val()) alert("Field must match button text");});
實際上,伺服器端和用戶端沒有任何區別。沒錯,這沒有按鈕點擊操作,也沒有向文字欄位鍵入的操作,但在一個更高的層面上,事件正在 發生。一個串連被建立,這是一個事件!資料通過串連進行接收,這也是一個事件!資料通過串連停止,這還是一個事件!
為什麼這種設定類型對 Node 很理想?JavaScript 是一種很棒的事件驅動程式設計語言,因為它允許使用匿名函數和閉包,更重要的是,任何寫過代碼的人都熟悉它的文法。事件發生時調用的回呼函數可以在捕獲事件處進行編寫。這樣可以使代碼容易編寫和維護,沒有複雜的物件導向架構,沒有介面,沒有過度設計的可能性。只需監聽事件,編寫一個回呼函數,其他事情都可以交給系統處理!
它對什麼有好處?
正如您此前所看到的,Node 非常適合以下情況:在響應用戶端之前,您預計可能有很高的流量,但所需的伺服器端邏輯和處理不一定很多。Node 表現出眾的典型樣本包括:
- RESTful API
提供 RESTful API 的 Web 服務接收幾個參數,解析它們,組合一個響應,並返回一個響應(通常是較少的文本)給使用者。這是適合 Node 的理想情況,因為您可以構建它來處理數萬條串連。它仍然不需要大量邏輯;它本質上只是從某個資料庫中尋找一些值並將它們組成一個響應。由於響應是少量文本,入站請求也是少量的文本,因此流量不高,一台機器甚至也可以處理最繁忙的公司的 API 需求。
- Twitter 隊列
想像一下像 Twitter 這樣的公司,它必須接收 tweets 並將其寫入資料庫。實際上,每秒幾乎有數千條 tweet 達到,資料庫不可能及時處理高峰時段所需的寫入數量。Node 成為這個問題的解決方案的重要一環。如您所見,Node 能處理數萬條入站 tweet。它能快速而又輕鬆地將它們寫入一個記憶體排隊機制(例如 memcached),另一個單獨進程可以從那裡將它們寫入資料庫。Node 在這裡的角色是迅速收集 tweet,並將這個資訊傳遞給另一個負責寫入的進程。想象一下另一種設計(常規 PHP 伺服器會自己嘗試處理對資料庫本身的寫入):每個 tweet 都會在寫入資料庫時導致一個短暫的延遲,因為資料庫調用正在阻塞通道。由於資料庫延遲,一台這樣設計的機器每秒可能只能處理 2000 條入站 tweet。每秒處理 100 萬條 tweet 則需要 500 個伺服器。相反,Node 能處理每個串連而不會阻塞通道,從而能夠捕獲儘可能多的 tweets。一個能處理 50,000 條 tweet 的 Node 機器僅需 20 台伺服器即可。
- 電子遊戲統計資料
如果您線上玩過《使命召喚》這款遊戲,當您查看遊戲統計資料時,就會立即意識到一個問題:要產生那種層級的統計資料,必須跟蹤海量資訊。這樣,如果有數百萬玩家同時線上玩遊戲,而且他們處於遊戲中的不同位置,那麼很快就會產生海量資訊。Node 是這種情境的一種很好的解決方案,因為它能採集遊戲產生的資料,對資料進行最少的合并,然後對資料進行排隊,以便將它們寫入資料庫。使用整個伺服器來跟蹤玩家在遊戲中發射了多少子彈看起來很愚蠢,如果您使用 Apache 這樣的伺服器,可能會 有一些有用的限制;但相反,如果您專門使用一個伺服器來跟蹤一個遊戲的所有統計資料,就像使用運行 Node 的伺服器所做的那樣,那看起來似乎是一種明智之舉。
Node Package Module
Node 的一個特性是 Node Package Module,這是一個內建功能,用於安裝和管理 Node 模組。它自動處理依賴項,因此您可以確定:您想要安裝的任何模組都將正確安裝並包含必要的依賴項。它還支援將您自己的模組發布到 Node 社區,假如您選擇加入社區並編寫自己的模組的話。您可以將 NPM 視為一種允許輕鬆擴充 Node 功能的方法,不必擔心這會破壞您的 Node 安裝。同樣,如果您選擇深入學習 Node,那麼 NPM 將是您的 Node 解決方案的一個重要組成部分。
結束語
Node 是一個程式,能夠完成 Apache 能夠完成的所有任務(藉助一些模組),而且,作為一個可以將其作為基礎進行構建的可擴充 JavaScript 平台,Node 還能完成更多的任務。
Node 完成了它提供高度可伸縮伺服器的目標。它使用了 Google 的一個非常快速的 JavaScript 引擎,即 V8 引擎。它使用一個事件驅動設計來保持代碼最小且易於閱讀。所有這些因素促成了 Node 的理想目標,即編寫一個高度可伸縮的解決方案變得比較容易。
學習
- Node.js 首頁 是瞭解這個應用程式的切入點。
- 在這裡下載 Node.js。您還將需要 Python。
- 瀏覽 Node.js API 頁。注意,不同發布的文法可能不同,因此,請仔細檢查您已下載的版本和正在瀏覽的 API。
- 參見 Node 模組頁,該頁列出了所有可用於 Node 中的模組。
- 搜尋 NPM,輕鬆擴充您的 Node 安裝的功能。
第二部分 使用 node.js 進行伺服器端 JavaScript 編程
作者:成 富 (居然是個湖南老鄉 北京 - 韶山 - 北京,MARK一下,http://www.cheng-fu.com )
模組化結構
node.js 使用了 CommonJS 定義的模組系統。不同的功能組件被劃分成不同的模組。應用可以根據自己的需要來選擇使用合適的模組。每個模組都會暴露一些公用的方法或屬性。模組使用者直接使用這些方法或屬性即可,不需要關係模組內部的實現細節。除了系統預置的多個模組之外,應用Team Dev也可以利用這個機制來將應用拆分成多個模組,以提高代碼的可複用性。
使用模組
在 node.js 中使用一個模組的方式是非常簡單的。使用某個模組之前需要首先聲明對它的依賴。在 JavaScript 代碼中可以直接使用全域函數 require()
來載入一個模組。如 require("http")
可以載入系統預置的 http
模組。而 require("./myModule.js")
用來載入與當前 JavaScript 檔案同一目錄下的 myModule.js
模組。如果使用 require()
的路徑以“/”開頭的話,則認為是模組 JavaScript 檔案在作業系統上的絕對路徑。如果不是這兩種情況的話,node.js 就會嘗試在當前 JavaScript 檔案的父目錄及其祖先目錄下的 node_modules
目錄下尋找。比如目錄 /usr/home/my.js
中調用了 require("other.js")
的話,node.js 會依次嘗試尋找下列檔案:/usr/home/node_modules/other.js
、/usr/node_modules/other.js
和 /node_modules/other.js
。
require()
方法的傳回值是該模組所暴露出來的公開 JavaScript 對象,包含了可供使用的方法和屬性。代碼清單 2 給出了模組的基本使用方式。
清單 2. 模組的基本使用方式
var greetings = require("./greetings.js"); var msg = greetings.sayHello("Alex", "zh_CN"); process.stdout.write(msg);
如 代碼清單 2 所示,一般是直接把 require()
方法的傳回值賦值給一個變數,在 JavaScript 代碼中直接使用此變數即可。greetings.js
模組暴露了一個 sayHello()
方法,當前 JavaScript 代碼直接使用了該方法。
事件驅動
開發過 Web 應用程式的人都熟悉瀏覽器中的事件處理機制。當對某個 DOM 元素上的某類事件感興趣的時候,只需要在該 DOM 元素上面註冊一個事件監聽器即可。如 ele.addEventListener("click", function() {})
就添加了一個對 click
事件的監聽器。當事件發生的時候,事件監聽器的 JavaScript 方法就會被調用。事件的處理方法是非同步執行的。這種非同步執行的方式非常適合於開發高效能並髮網絡應用。實際上,目前的高效能並發應用開發一般有兩種做法:第一種是使用多線程的機制,另外一種就是採用基於事件驅動的方式。多線程的問題在於應用開發起來難度較高,很容易出現線程饑餓或是死結等問題,對開發人員提出了更高的要求。而事件驅動的方式則更加靈活,很容易為 Web 開發人員所理解和使用,也不存線上程死結等問題。依託於效能強大的 Google V8 引擎和先進的事件 I/O 架構,node.js 可以成為建立高效能伺服器端應用的良好基礎。
基於 node.js 開發應用與開發 Web 應用程式有相似的編程模型。很多模組都會暴露出一些事件,使用這些模組的代碼通過註冊事件監聽器的方式來添加相應的處理邏輯。代碼清單 4 中給出了一個簡單的 HTTP Proxy 伺服器的實現代碼。
清單 4. HTTP Proxy 伺服器
var http = require("http"); var url = require("url"); http.createServer(function (req, res) { var urlObj = url.parse(req.url, true); // 擷取被代理的 URL var urlToProxy = urlObj.query.url; if (!urlToProxy) { res.statusCode = 400; res.end("URL 是必須的。"); } else { console.log("處理代理請求:" + urlToProxy); var parsedUrl = url.parse(urlToProxy); var opt = { host : parsedUrl.hostname, port : parsedUrl.port || 80, path : (parsedUrl.pathname || "") + (parsedUrl.search || "") + (parsedUrl.hash || "") }; http.get(opt, function(pres) { // 請求被代理 URL 的內容 res.statusCode = pres.statusCode; var headers = pres.headers; for (var key in headers) { res.setHeader(key, headers[key]); } pres.on("data", function(chunk) { res.write(chunk); // 寫回資料 }); pres.on("end", function() { res.end(); }); }); } }).listen(8088, "127.0.0.1"); console.log("Proxy 伺服器已經在 8088 連接埠啟動。");
整個Proxy 伺服器的實現比較簡單。首先通過 http
模組中的 createServer()
方法用來建立一個 HTTP 伺服器,再通過 listen()
方法就可以讓該 HTTP 伺服器在特定連接埠監聽。在 createServer()
方法中傳入的參數是 HTTP 要求的回應程式法。實際上,每個 HTTP 要求都是對應於 HTTP 伺服器上的一個 request
事件。代碼清單 4 中的 HTTP 伺服器建立部分實際上等價於 代碼清單 5 中給出的實現方式。
清單 5. 使用事件機制的 HTTP 伺服器建立方式
var server = http.createServer(); server.on("request", function(req, res) { });
在請求的處理方法裡面,通過 http.get()
方法來擷取被代理 URL 的內容。這裡同樣採用了基於事件的處理方式。pres.on("data", function(chunk) {})
在 pres
的 data
事件上添加了一個處理方法。該方法的作用是當擷取到被代理 URL 的內容的時候,就把擷取到的內容寫回到原始 HTTP 要求的響應中。對於 end
事件的處理也是同樣的。在使用 node.js 進行開發的時候,會經常遇到這種使用事件處理方法和回調方法的情境。
第三部分:JavaScript 現代特性 (與java、groovy、ruby比較)
Andrew Glover, 作家和開發人員, Beacon50 Java 開發 2.0: 面向 Java 開發人員的 JavaScript
Java 開發人員一直不看好 JavaScript,因為用它來編程顯得太過輕量級了,而作為指令碼又顯得太過笨重。但有人還在使用 JavaScript,因為它是 GWT 與 Node.js 之類優秀 Web 技術的基礎。在本期 Java 開發 2.0 中,Andrew Glover 解釋了為何 JavaScript 是現代 Java 開發人員的重要工具。然後介紹了構建當前 Web 的一流應用程式所需的文法,包括 JavaScript 變數、類型、函數、和類。
在 Chrome 中使用 JavaScript: F12
我喜歡 Chrome 漂亮的 JavaScript 控制台。就像 Ruby's IRB 或者 Python's shell,Chrome 提供不需要 Web 頁面的用於瀏覽 JavaScript 的互動式環境。
本人註:調試以下程式清單更好的方式是用node.js 提供的node命令列工具。cmd > node
JavaScript 變數
JavaScript 是一個比較容易使用的語言,它能容忍很多編程錯誤,並仍然能在載入的 Web 頁中執行。JavaScript 元素經常沒有任何提示就失敗了,這大部分都是好訊息。如果 JavaScript 語言草率地禁止頁面載入,那麼早期的 Web 將是一片混亂。也就是說,當我們利用 JavaScript 來做我們喜歡做的事時(比如非同步更新頁面狀態),草率使用 JavaScript 將付出代價。由於這個原因,Java 開發人員應該花時間真正瞭解 JavaScript 文法的特定方面。
JavaScript 變數的處理對理解來說很重要。例如,您想定義一個變數 foo
,可以直接定義或者通過 var
聲明,如清單 1 所示:
清單 1. 變數 foo
foo = 'foo'var bar = 'bar'
清單 1 中的 foo
是 JavaScript 中的有效變數。但它缺少 var
聲明,這是個全域變數。因此利用 var
定義的變數是有範圍的(例如,在定義它的函數中)。
通常,全域變數比較麻煩。它們很容易造成變數使用的混亂,因為全域變數可在 JavaScript 應用程式中的任何地方進行訪問和改變,這將導致潛在 bug。因此,當在 JavaScript 中編程時,不要忘了對變數應用 var
。
基元和對象
儘管 JavaScript 不夠完善,但在類型方面十分簡單。事實上,JavaScript 僅有 4 個基本類型,其中三個是基元。JavaScript 的基元類型是 Number
、String
、和 Boolean
。您可通過 JavaScript typeof
運算子來在操作中查看這些類型。
我們來一起看一下該問題。在 Chrome's JavaScript 中,輸入清單 2 中的內容:
清單 2. Activating types
var string = "test"typeof string
可以看到控制台輸出值 “string
”。還要注意在 JavaScript 中分號是可選的。正如在流行的語言當中一樣,string
通過引號來劃定;因此,數字通過數字來劃定。Booleans 通過值 true 或者 false 來劃定,沒有分號。
清單 3. JavaScript truth 與 numbers
var aNumber = 10var anotherNumber = 0.99var aBool = truevar notABoolean = "false"
您會注意到,JavaScript 不區分數字類型;數字就是數字,只不過具有不同格式。
JavaScript 還支援通用對象,它本身具有執行個體類型,比如 Array
,如清單 4 所示:
清單 4. Array 的執行個體
> var myArray = ["Hello", 1, true]> typeof myArray"object"> myArray instanceof Arraytrue
JavaScript 中的 Array
s 更像其他語言中的列表:可不必限制大小來進行建立,可以儲存任何您輸入的內容。比如在 Ruby 或者 Groovy 中,JavaScript Array
s 可通過文字文法來建立:[]
。更重要的是,就像在其他支援列表的語言一樣,希望在 JavaScriptArray
s 支援方法(如清單 5 所示):
清單 5. Array 方法
> var myArray = ["Hello", 1, true]> myArray[0]"Hello"> myArray.length3> myArray.pop()true> myArray["Hello", 1]> myArray.pop()1> myArray["Hello"]> myArray.push("pushed")2> myArray["Hello", "pushed"]
可通過 Array
的位置來擷取其值,從零開始。Array
s 支援push
和 pop
操作,其中 push
增加項目(在其最後一個位置)而 pop
移除項目(就像堆棧,從最後一個開始)。
Array
s 還支援迭代,如清單 6 所示:
清單 6. 通過 Array 迭代
> var myArray = [1,2]> for(var i = 0; i < myArray.length; i++) { console.log(myArray[i]) }12
類型強制
JavaScript 不僅是一個弱類型的語言 — 它比 Ruby 或者 Groovy 更弱 !JavaScript 可強制變數在代碼的特定位置為任何類型。這符合 JavaScript 最初設想:Web 頁面互動。JavaScript 不應草率地禁止使用者讀取線上文章!
類型強制不僅限於 JavaScript,但是 JavaScript 的特點是非常靈活。這是好事還是壞事,取決於您怎麼看。JavaScript 的鬆散可能會隱藏缺陷,就像全域變數一樣。
例如,先定義 Array
然後無意中嘗試用於進行一些數字操作,甚至是一些 String
級聯,如清單 7 所示:
清單 7. JavaScript 類型的靈活性
> var myArray = [1,2]> console.log(2 * myArray)> console.log("A" + myArray)
在本例中,第一個日誌訊息將會列印 NaN
,而第二個將會列印 A1,2
。在兩個例子中,該代碼能 “正常運行”,因為沒有任何錯誤發生 — JavaScript 只是不停地運轉。這是極端情況下的弱類型。Ruby 中同樣的代碼不會這樣運行,如清單 8 所示:
清單 8. Ruby 不採用那種方式
> array = ["A", "B"]> ans = 2 * array
清單 8 中的 Ruby 代碼將會出錯:
TypeError: Arraycan't be coerced into Fixnum
如果嘗試向 array
增加 "A"
,情況將是:
TypeError: can't convertArray into String
如果在 Groovy 中嘗試相同的操作,將會得到如下結果:
groovy.lang.MissingMethodException: No signature of method: java.lang.Integer.plus() is applicable for argument types: (java.util.ArrayList) values:[[A, B]]
因此,您可以在操作中看到不同層面的弱類型。顯而易見,如果有一個度量類型強弱的標準,JavaScript 將會是它們當中最弱的!
JavaScript 函數
JavaScript 函數,類似於 Java 方法,是用於定義和封裝可重用行為的結構。JavaScript 中的函數看上去很像 Groovy 的閉包。在 JavaScript 中的函數是對象。事實上,他們是第一類對象,不像 Java 代碼中的方法。因為 JavaScript 函數是對象,所以它可傳遞給其他函數,並可被調用。
利用 function
關鍵字來定義函數。就像 Java 語言中的方法聲明一樣,可以指定參數,還可從 JavaScript 函數返回一些內容。與動態語言不同,比如 Groovy 或者 Ruby,其中返回調用是可選的(這樣任何方法的最後一行將返回),如果想要得到傳回值,JavaScript 的函數中必須使用 return 語句;否則,將不會傳回值。
可以像在 Groovy 中調用閉包一樣來在 JavaScript 中調用函數。在清單 9 中,定義了一個沒有參數的簡單函數。其目的是在 Chrome 的 JavaScript 控制台中列印 “blah”。
清單 9. 在 JavaScript 中定義並調用函數
> function blah() { console.log("blah"); }> blah() //prints blah> blah.call() //prints blah> blah.apply() //prints blah
可通過方法 call
或者方法 apply
直接調用有括弧(也就是 ()
)的函數。在這裡展示了首個無類函數對象。清單 10 展示了在函數blah
中調用 garbage 方法時的情形:
清單 10. JavaScript 中函數作為對象
> blah.foo()
在本例中,錯誤訊息說明 foo
不是一個定義的方法,像這樣:
TypeError: Object function blah() { console.log("blah"); } has no method 'foo'
現在再次讀取錯誤訊息。看到 foo
不是 已定義的,這意味著如果它 被 定義,一切就會正常。
JavaScript 中的類
JavaScript 支援原語,我們曾討論過。它也支援對象,比如 Array
。JavaScript 不支援類 — 至少在經典 Java 語言中不支援。因為 JavaScript 是基於原型的語言,您不能定義類:相反,通過複製 現有對象來重用行為。因此,在 JavaScript 中,不定義類對象,而在函數中進行定義,然後利用嵌套函數來定義行為 — 有些已在運行中見過。
想要類比一個類,您必須定義一個函數。可以給其一個名稱(也就是一個類名),指定參數(和在建構函式中一樣),甚至可以使用關鍵字 .this
,這意味著在函數範圍內引用變數。更何況,內建函式可以具有別名,看上去像方法調用一樣。
為了進行說明,在清單 11 中,我將建立一個 Message
原型(也稱為一個類),這非常簡單。我將提供一些參數(訊息從何處來,發給誰,以及訊息本身),而且該類將會以 JSON 格式呈現該訊息。
清單 11. JavaScript 中函數作為類
function Message(to, from, msg){ this.to = to; this.from = from; this.msg = msg; this.asJSON = function(){ return "{'to':'" + this.to + "', 'from':'" + this.from + "', 'message':'" + this.msg + "'}"; }}
在 清單 11 中,我定義了一個 Message
函數 — 帶有一個名字和幾個屬性的對象;即,to
、from
、和 msg
。然後我定義了一個屬性(asJSON
)指向內建函式,其任務是將 JSON 訊息用字串表示。
注意,還可在 Web 頁面中定義該 “類”,利用 Chrome 來進行載入,開啟 JavaScript 控制台,並互動式地進行使用。這就是清單 12 所述的:
清單 12. 在 JavaScript 中使用類
> var message = new Message('Andy', 'Joe', 'Party tonight!');> message.asJSON();"{'to':'Andy', 'from':'Joe', 'message':'Party tonight!'}"
此代碼很像 Groovy 代碼,甚至像 Java 代碼(如果不考慮 var
),不是嗎?實際上,完全可以利用 OOP 技術來構建 JavaScript 應用程式(也就是,程式包含帶有資料和相互互動方法的對象)。
結束語
我希望這篇文章能夠推翻早已淘汰的 JavaScript 語言無用論。事實上,它的功能十分強大,而且具有很多文法糖,可以像 Groovy 和 Ruby 之類新的語言一樣方便。在 90 年代使得 JavaScript 流行的一些特性,在今天也是可取的。
鑒於 Web 對越來越多的 Java 應用程式開發的重要性,以及 JavaScript 作為瀏覽器安全色語言的獨特地位,每個 Java 程式員都應當熟悉 JavaScript。 瀏覽器(不論是在電腦中,或者在行動裝置、電話、或者平板電腦中)是越來越多使用者與應用程式互動的方法。JavaScript 是所有伺服器端語言中最常見的媒介。此外,對 JavaScript 有一定理解,將會使您成為任何語言領域(包括您最熟悉的語言)的一名優秀的程式員。
擴充閱讀:
JavaScript 開發套件 http://www.ibm.com/developerworks/cn/web/lp/jstoolkit/
第四部分:CoffeeScript
摘自: http://article.yeeyan.org/view/260164/251745 你的第一杯CoffeeScript,第1部分:入門
The Little Book on CoffeeScript - 文法island205 本文譯自The Little Book on CoffeeScript的第1章。該書是GitHub上的一個開源項目,採用MIT授權協議。遂將其翻譯過來,以便方便更多人接觸CoffeeScript,沿用MIT授權協議。譯文有不妥之處,請指正。
CoffeeScirpt是什嗎?
CoffeeScript是一門小巧的語言,會編譯為JavaScript。它的文法風格受到了Ruby和Python影響,很多特性都借鑒於這兩種語言。我們寫作本書的目的在於協助你學習CoffeeScript,明白最佳實務是什麼,以及協助你開始建立有意思的用戶端程式。這本書很小,僅僅只有五章,但是對與CoffeeScript這門小語言來說已足夠。
CoffeeScript*不是*JavaScript的超集,因此儘管你可以在CoffeeScript中的使用外部的JavaScript類庫,但是如果你在沒有轉化之前而直接編譯當前的JavaScript的話,會出現語法錯誤。編譯器會把CoffeeScript代碼轉化為相對於的JavaScript,這樣在運行時就不需要解釋了。
首先澄清一些誤解。由於處理執行階段錯誤需要JavaScript相關的知識,要寫CoffeeScript就得瞭解JavaScript。但是話說回來,執行階段錯誤通常比較明顯,並且到目前位置,我沒覺得從JavaScript映射到CoffeeSCript會有什麼問題。第二個問題是我經常聽到CoffeeScript相關的話題是速度。即,CoffScript編譯後的JavaScript運行起來相比與之等價的純JavaScript代碼要慢。但實際情況證明並不是問題。CoffeeScript看起來與徒手寫的JavaScript代碼運行速度相當,甚至更快。
CoffeeScript的劣勢是什嗎?是的,在你和JavaScript之間介多了編譯這一步。CoffeeScript也在嘗試儘力通過產生優雅的可讀性強的JavaScript,以及在伺服器端整合自動編譯來彌補這個問題。另外一個缺陷是,作為一個新的語言,事實上現階段社區也還比較小,想找個懂這門語言的合伙人會花費你的大量你的時間。當然,CoffeeScript發展迅猛,相關的IRC列表也是人才濟濟,如果你有什麼問題的話,都會得到迅速的解答。
CoffeeScript的用途並不僅限於瀏覽器,把它用在JavaScript實現的服務端也非常不錯,比方說在 Node.js上。還有,CoffeeScript越來越廣泛,有更多的整合,比方說它已經是Rails3.1的標配。現在正是進入CoffeeScript學習的時機。你現在為學習這門語言付出的時間在以後會以為你節約大量的時間作為回報的。
安裝
你是否曾希望能夠通過命令列來運行JavaScript?反正我是沒有,不過CoffeeScript可能會改變現狀。有了Node.js,你可以通過命令列來運行JavaScript,或是把JavaScript作為執行指令碼的組成部分來運行。Node.js的這一主要功能允許在命令列上執行CoffeeScript代碼,其提供了(使用CoffeeScript編寫的)CoffeeScript編譯器所需要的運行時。
第一步是安裝Node.js,有幾種安裝選擇;你可以編譯原始碼,或是運行可用於各種系統的安裝程式之一。通過命令列運行node -v來確認Node.js已經安裝並且已位於路徑中。
Node.js給你帶來了一個額外的收穫:節點包管理器(NPM)。通過命令列運行npm -v確認NPM已經安裝並已位於路徑中之後,就可以按照如下步驟來使用NPM安裝CoffeeScript了。
1. 通過命令列運行npm install --global coffee-script。
--global標記讓CoffeeScript可為全系統所用而不僅是為某個特定項目所用。
D:\work\js>npm install --global coffee-scriptnpm http GET https://registry.npmjs.org/coffee-scriptnpm http 200 https://registry.npmjs.org/coffee-scriptnpm http GET https://registry.npmjs.org/coffee-script/-/coffee-script-1.2.0.tgznpm http 200 https://registry.npmjs.org/coffee-script/-/coffee-script-1.2.0.tgzC:\Users\Administrator\AppData\Roaming\npm\coffee -> C:\Users\Administrator\AppData\Roaming\npm\node_modules\coffee-script\bin\coffeeC:\Users\Administrator\AppData\Roaming\npm\cake -> C:\Users\Administrator\AppData\Roaming\npm\node_modules\coffee-script\bin\cakecoffee-script@1.2.0 C:\Users\Administrator\AppData\Roaming\npm\node_modules\coffee-script
2. npm命令應該輸出諸如/usr/bin/coffee -> /usr/lib/node_modules/coffee-script/bin/coffee一類的資訊。
NPM在 /usr/bin中建立了一個捷徑,所以現在coffee這一可執行檔就處在正確的路徑中了,這是CoffeeScript的編譯器和解譯器。
3. 若要驗證coffee這一執行檔案已位於路徑中,通過命令列運行coffee -v。
還剩最後一步來確認CoffeeScript的環境已被正確設定。為了讓CoffeeScript對於任何啟動的Node.js進程來說都是可用的,你需要把它加入到Node.js所謂的NODE_PATH中,在遇到未識別的函數時,Node.js會通過搜尋NODE_PATH來擷取一些模組(庫)。
就本文中的例子來說,主要是把Node.js用作CoffeeScript可執行檔的運行時。最容易的做法是簡單地把所有的NPM模組都添加到NODE_PATH中,若要找出NPM模組所處的位置,輸入 npm ls -g,你需要添加一個把NODE_PATH指向這一位置的環境變數。例如,如果npm ls -g的輸出是/usr/lib,則模組位於/usr/lib/node_modules目錄下,若要設定一個NODE_PATH環境變數,運行export NODE_PATH=/usr/lib/node_modules。
你可以通過把上面的命令放入到啟動指令碼(比如說~/.bash_profile)中來進一步簡化這些事情。若要驗證所做的修改,執行Node來啟動一個Node.js外殼程式,然後輸入require('coffee-script'),Node.js外殼程式應該就會載入CoffeeScript庫。如果執行沒問題的話,CoffeeScript環境就是隨時可用的了。現在你可以從編譯器開始,啟動CoffeeScript的探索之旅了。