一個Node.js初學者的“班門弄斧

flowerwxc發表於2015-12-25

凌晨三點,當啟動控制檯(console)的時候,手已經按在鍵盤上了。黑色背景上出現了扎眼的提示,渴求接收命令。想折騰下node.js?node,js有一個令人興奮的訊息:它可以在任何地方執行。這一點讓棧有了多種不同的嘗試方式。對任何一個經驗豐富的人,使用命令列來執行都充滿了樂趣。我最喜歡的是可以在命令列的安全網路中審閱棧。牛逼的是我們仍然在說javascript,所以你們中的大部分不會有任何問題。那麼,為什麼不開始在console裡啟動node呢?

本文中,我會向你介紹Node.js。我的目標是在一些有難度的地方能看下出彩的部分。本文是一篇概述留存在console裡的棧的媒介。如果你想看看適合新手的Node.js指南,不妨看看SitePoint的優質課程Node.js:An Introduction.

為什麼是Node.js?

在我們開始前,先看看讓Node.js鶴立雞群的”花邊“新聞: •為非阻塞I/O而設計 •為非同步操作而設計 •驅動Chrome的V8 JavaScript

你可能已經從很多地方知道以上幾點了,但是這些到底是什麼意思呢?你可以將Node.js看作是一個為javascript提供了大量API的引擎。在傳統的同步程式設計方式中,當你有一個I/O操作時,API在執行下一條指令前會處於等待狀態。I/O操作就是,舉個例子,讀取檔案或發出一個網路請求。Node.js不同,它從設計之初就是為了非同步操作。在當下的程式設計環境,這一點有巨大的優勢。你還能想起你最後一次因為一個更快的單核處理器而換電腦是什麼時候嗎?處理器的數量和更快的硬碟更加重要。

接下來的文章中,當你看到>這個提示標誌時,意味著需要按enter並輸入下一條指令。此外,在執行本文中的程式碼時,你需要開啟CLI,並執行指令node(注:需要先下載並安裝node.js),說著就開始吧!

回撥(Callbacks)

首先輸入下列函式:

1.>function add(a,b,callback){var result = a+b;callback(result);}

對網路新手,JavaScript中的callback可能很陌生。這看起來並不是一個典型的OOP方法。在JavaScript中,函式(function)是一個物件,物件可以接受其它物件作為引數。JavaScript並不在乎物件是什麼,所以一個函式可以接受另一個是函式的物件。數字引數數量,在回撥函式中通過add()由兩個變為一個。回撥系統相當的強大,因為它支援封裝並將現實封裝隱藏。在Node.js中,你會發現有許多APIs將一個回撥作為引數。可以把回撥想象成是一個代表。程式隱藏在裡面,而這個代表就是經授權被派遣代替別人的人。所以回撥像是派一個人來完成差事。給一系列引數,就像列出一個購物單,然後他們就自己去完成任務了。

為了玩轉add():

1.> add(2, 3, function (c) { console.log('2 + 3 = ' + c) }); 2.> add(1, 1, function (c) { console.log('Is 1 + 1 = 3? ' + (c === 3)); });

多嘗試一些回撥的新奇玩法吧。它是Node.js中一些重要API的基礎。

非同步操作

通過回撥,我們可以開始編寫一些非同步API了,舉個例子(其實這個季節似乎沒有栗子可以舉):

1.> function doSomething (asyncCallback) { asyncCallback(); } 2.> doSomething(function () { console.log('This runs synchronously.'); });

這個特例中有一個同步操作,但是對於JavaScript中的非同步操作,我們已經萬事俱備了。asyncCallback,可以在一些執行緒中被延遲:

1.> function doSomething (asyncCallback) { setTimeout(asyncCallback, Math.random() + 1000); } 2.> doSomething(function () { console.log('This runs asynchronously.'); }); console.log('test');

我用了setTimeout在當前執行緒中延遲操作。timeout並沒有給定時間,所以我使用Math.random()來設一個變化的時間。取名為doSomething,然後用console.log(‘test’)來顯示被延後的操作。在1~2秒鐘後,就可以在螢幕上看懂啊彈出的訊息了。我舉例的非同步回撥是不可預測的。Node.js將其放在時間表裡,繼續前面的程式。時間到了後,Node,js剛好執行到這個非同步操作的時間,然後呼叫這個回撥。所以你必須腦中明晰各種回撥以更好的理解Node.js。

一句話,在JavaScript中,回撥並不總是它看起來的樣子。

讓我們做一些更有趣的事(原諒我沒發現哪裡有趣)。不如試試在Node.js中查詢DNS?

1.> dns.lookup('bing.com', function (err, address, family) { console.log(' Address: ' + address + ', Family: ' + family + ', Err: ' + err); });

這個回撥返回err,address,family三個物件,重點是返回值被當作引數傳遞給回撥。所以這個傳統的像var result= fn('bing.com')不太一樣。在Node.js中,必須通過回撥和非同步來獲得總體概況。你可以去看看[DNS Node.js API][2]瞭解更多細節。下圖是我控制檯裡DNS lookup的樣子:

檔案I/O

現在繼續,在Node,js中怎麼執行檔案寫入寫出呢?想象這個場景:你開啟一個檔案,讀取檔案內容然後寫入新的內容。在現代工藝的電腦上I/O-bound(天,我完全不知道這怎麼翻譯,知道的私信我,我改)操作很慢。CPU處理速度很快,RAM也很快。但是,硬碟的讀取或寫入速度很慢。因此當程式同步執行I/O-bound操作時,執行的很慢。替代方法是採用非同步,比如:

1.> var fs = require('fs'); 2.> fs.writeFile('message.txt', 'Hello Node.js', function () { console.log('Saved.'); }); console.log('Writing file...');

因為操作是非同步的,因此你會先看到“writing file...."然後看到檔案被儲存。回撥函式最普通的使用方式十分適合這個API。你可以看看文件[file System API][6]這部分。讀取檔案又是怎樣做呢?你能通過前例猜猜麼?給你一點小提示,回撥引數是err和data.我建議你試試。

答案:

1.> fs.readFile('message.txt', function(err, data) { console.log(data); });

你也可以傳入一個encoding項來讀取檔案中utf-8內容:

1.> fs.readFile('message.txt', {encoding: 'utf-8'}, function(err, data) { console.log(data); });

Node.js回撥函式的非同步I/O操作看起來很完美。

Web 伺服器

那麼在Web伺服器上會怎樣呢?任何一個優秀的Exposé(MacOS上的多視窗管理工具?)都必須執行一個Web服務端。假設有一個叫做creatServer的API,這個API有一個引數是request和response的回撥。你可以在文件中看看HTTP API。你能想到是什麼樣子嗎?你需要Http模組。去console裡試試吧。

答案:

1.> var http = require('http'); 2.> var server = http.createServer(function (request, response) { response.end('Hello Node.js'); });

細想一下web,它是一個帶有請求和響應模組的客戶端-伺服器。Node.js有一個來自客戶端的request物件和一個來自伺服器的response物件。因此棧通過簡單的回撥原理解決web難題。還記得它是非同步的嗎?我希望你能把零散的回憶綜合一下,如果你看了API文件,我們現在做的很那個差不多。我們載入一個模組,告訴它要做什麼並給它一個引數。回撥就會像一個代表那樣工作:執行一個有一系列引數的特殊任務。

當然,如果我們不能在瀏覽器中看到它,那麼一切都是無意義的。在CLI中新增:

1.server.listen(8080);

然後將你最愛的瀏覽器指定為:localhost:8080,我用的是Edge。

假設requset物件有一堆可獲取的資訊。為了重新連上server,先將其關閉(bring it down):

1.> server.close(); 2.> server = http.createServer(function (request, response) { response.end(request.headers['user-agent']); }); server.listen(8081);

將瀏覽器指定為:localhost:8081.header提供給你來自瀏覽器的user-agent資訊。我們可以遍歷header物件:

1.> server.close(); 2.> server = http.createServer(function (request, response) { Object.keys(request.headers).forEach(function (key) { response.write(key + ': ' + request.headers[key] + ' '); }); response.end(); }); server.listen(8082);

這次 將瀏覽器指定為:localhost:8082。一旦完成之後,記得關閉。否則命令列可能會產生奇怪的執行結果。

1.> server.close();

你已經學會啦,通過命令列來建立web伺服器。我希望你喜歡這趟node迷幻之旅。

總結

Node.js很適合現代的解決方案,因為其十分簡單而輕便。它充分利用了現代硬體非模組設計的優點。它包含了web固有的客戶端-伺服器模組。最好都用我們喜歡的JavaScript執行。引起我興趣的是棧的關鍵部分並不前沿。網頁來自早期構建的輕量級易接入模組。如果有時間,我建議你看看Tim Berners-Lees’ Design Principles.。原則中為Node.js的least power applies提供了JavaScript選項。

原文連結:http://www.gbtags.com/gb/share/9522.htm

相關文章