Node.js 是一個基於 Chrome V8 引擎的 JavaScript 運行環境,是一個可讓 JavaScript 運行在服務器端的平臺javascript
Node.js 使用了一個事件驅動、非阻塞式 I/O 的模型,使其輕量又高效。Node.js 的包管理器 npm,是全球最大的開源庫生態系統。css
採用了單線程、異步式I/O、事件驅動式的程序設計模型html
I/O(input/output),即輸入/輸出端口。每一個設備都會有一個專用的I/O地址,用來處理本身的輸入輸出信息。java
ctrl + c - 退出當前終端。node
ctrl + c 按下兩次 - 退出 Node REPL。jquery
ctrl + d - 退出 Node REPL.git
向上/向下 鍵 - 查看輸入的歷史命令github
tab 鍵 - 列出當前命令web
help - 列出使用命令數據庫
break - 退出多行表達式
.clear - 退出多行表達式
save filename - 保存當前的 Node REPL 會話到指定文件
load filename - 載入當前 Node REPL 會話的文件內容。
Node.js 事件循環
Node.js 是單進程單線程應用程序,可是經過事件和回調支持併發,因此性能很是高。
Node.js 的每個 API 都是異步的,並做爲一個獨立線程運行,使用異步函數調用,並處理併發。
Node.js 基本上全部的事件機制都是用設計模式中觀察者模式實現。
Node.js 單線程相似進入一個while(true)的事件循環,直到沒有事件觀察者退出,每一個異步事件都生成一個事件觀察者,若是有事件發生就調用該回調函數.
觀察者模式:觀察者模式定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態發生變化時,會通知全部觀察者對象,使它們可以自動更新本身。
Node.js 使用事件驅動模型,當web server接收到請求,就把它關閉而後進行處理,而後去服務下一個web請求。
當這個請求完成,它被放回處理隊列,當到達隊列開頭,這個結果被返回給用戶。
這個模型很是高效可擴展性很是強,由於webserver一直接受請求而不等待任何讀寫操做。(這也被稱之爲非阻塞式IO或者事件驅動IO)
在事件驅動模型中,會生成一個主循環來監聽事件,當檢測到事件時觸發回調函數。
整個事件驅動的流程就是這麼實現的,很是簡潔。有點相似於觀察者模式,事件至關於一個主題(Subject),而全部註冊到這個事件上的處理函數至關於觀察者(Observer)。
Node.js 有多個內置的事件,咱們能夠經過引入 events 模塊,並經過實例化 EventEmitter 類來綁定和監聽事件
Node.js 中所謂的 JavaScript 只是 Core JavaScript,或者說是 ECMAScript 的一個實現,不包含 DOM(文檔對象模型)、BOM(瀏覽器對象模型) 或者 Client JavaScript(客戶端腳本)。這是由於 Node.js 不運行在瀏覽器中,因此不須要使用瀏覽器中的許多特性。
Node.js 能作什麼
具備複雜邏輯的網站;
基於社交網絡的大規模 Web 應用;
Web Socket 服務器;
TCP/UDP 套接字應用程序;
命令行工具;
交互式終端程序;
帶有圖形用戶界面的本地應用程序;
單元測試工具;
客戶端 JavaScript 編譯器。
TCP/UDP協議
TCP (Transmission Control Protocol)和UDP(User Datagram Protocol)協議屬於傳輸層協議。其中TCP提供IP環境下的數據可靠傳輸,它提供的服務包括數據流傳送、可靠性、有效流控、全雙工操做和多路複用。經過面向鏈接、端到端和可靠的數據包發送。通俗說,它是事先爲所發送的數據開闢出鏈接好的通道,而後再進行數據發送;而UDP則不爲IP提供可靠性、流控或差錯恢復功能。通常來講,TCP對應的是可靠性要求高的應用,而UDP對應的則是可靠性要求低、傳輸經濟的應用。TCP支持的應用協議主要有:Telnet、FTP、SMTP等;UDP支持的應用層協議主要有:NFS(網絡文件系統)、SNMP(簡單網絡管理協議)、DNS(主域名稱系統)、TFTP(通用文件傳輸協議)等。
Node.js 用異步式 I/O 和事件驅動代替多線程,帶來了可觀的性能提高。Node.js 除了使用 V8 做爲JavaScript引擎之外,還使用了高效的 libev 和 libeio 庫支持事件驅動和異步式 I/O。
CommonJS 爲了統一javascript在瀏覽器外的實現
CommonJS 規範包括了模塊(modules)、包(packages)、系統(system)、二進制(binary)、控制檯(console)、編碼(encodings)、文件系統(filesystems)、套接字(sockets)、單元測試(unit testing)等部分。目前大部分標準都在擬定和討論之中,已經發布的標準有
Modules/1.0、Modules/1.一、Modules/1.1.一、Packages/1.0、System/1.0。
REPL (Read-eval-print loop),即輸入—求值—輸出循環
例子:
$ node
> console.log('Hello World');
Hello World
Undefined
Supervisor工具
在開發 Node.js 實現的 HTTP 應用時會發現,不管你修改了代碼的哪一部份,都必須終止
Node.js 再從新運行纔會奏效。這是由於 Node.js 只有在第一次引用到某部份時纔會去解析腳本文件,之後都會直接訪問內存,避免重複載入,
Node.js的這種設計雖然有利於提升性能,卻不利於開發調試,因
爲咱們在開發過程當中老是但願修改後當即看到效果,而不是每次都要終止進程並重啓。
supervisor 能夠幫助你實現這個功能,它會監視你對代碼的改動,並自動重啓 Node.js。
使用方法很簡單,首先使用 npm 安裝 supervisor:
$ npm install -g supervisor
若是你使用的是 Linux 或 Mac,直接鍵入上面的命令極可能會有權限錯誤。緣由是 npm須要把 supervisor 安裝到系統目錄,須要管理員受權,可使用 sudo npm install -gsupervisor 命令來安裝。
接下來,使用 supervisor 命令啓動 app.js:
$ supervisor app.js
異步式 I/O 與事件式編程
Node.js 最大的特色就是異步式 I/O(或者非阻塞 I/O)與事件緊密結合的編程模式。這種模式與傳統的同步式 I/O 線性的編程思路有很大的不一樣,由於控制流很大程度上要靠事件和回調函數來組織,一個邏輯要拆分爲若干個單元。
阻塞與線程
什麼是阻塞(block)呢?線程在執行中若是遇到磁盤讀寫或網絡通訊(統稱爲 I/O 操做),一般要耗費較長的時間,這時操做系統會剝奪這個線程的 CPU 控制權,使其暫停執行,同時將資源讓給其餘的工做線程,這種線程調度方式稱爲 阻塞。當 I/O 操做完畢時,操做系統將這個線程的阻塞狀態解除,恢復其對CPU的控制權,令其繼續執行。這種 I/O 模式就是一般的同步式 I/O(Synchronous I/O)或阻塞式 I/O (Blocking I/O)。
相應地,異步式 I/O (Asynchronous I/O)或非阻塞式 I/O (Non-blocking I/O)則針對全部 I/O 操做不採用阻塞的策略。當線程遇到 I/O 操做時,不會以阻塞的方式等待 I/O 操做的完成或數據的返回,而只是將 I/O 請求發送給操做系統,繼續執行下一條語句。當操做系統完成 I/O 操做時,以事件的形式通知執行 I/O 操做的線程,線程會在特定時候處理這個事件。爲了處理異步 I/O,線程必須有事件循環,不斷地檢查有沒有未處理的事件,依次予以處理。
阻塞模式下,一個線程只能處理一項任務,要想提升吞吐量必須經過多線程。而非阻塞模式下,一個線程永遠在執行計算操做,這個線程所使用的 CPU 核心利用率永遠是 100%,I/O 以事件的方式通知。在阻塞模式下,多線程每每能提升系統吞吐量,由於一個線程阻塞時還有其餘線程在工做,多線程可讓 CPU 資源不被阻塞中的線程浪費。而在非阻塞模式下,線程不會被 I/O 阻塞,永遠在利用 CPU。多線程帶來的好處僅僅是在多核 CPU 的狀況下利用更多的核,而Node.js的單線程也能帶來一樣的好處。這就是爲何 Node.js 使用了單線程、非阻塞的事件編程模式
單線程事件驅動的異步式 I/O 比傳統的多線程阻塞式 I/O 究竟好在哪裏呢?簡而言之,異步式 I/O 就是少了多線程的開銷。對操做系統來講,建立一個線程的代價是十分昂貴的,須要給它分配內存、列入調度,同時在線程切換的時候還要執行內存換頁,CPU 的緩存被清空,切換回來的時候還要從新從內存中讀取信息,破壞了數據的局部性。
回調函數
Node.js 中如何用異步的方式讀取一個文件
//readfile.js
var fs = require('fs');
fs.readFile('file.txt', 'utf-8', function(err, data) {
if (err) {
console.error(err);
} else {
console.log(data);
}
});
console.log('end.');
運行的結果以下:
end.
Contents of the file.
注:function()是回調函數,在Node.js 中,異步式 I/O 是經過回調函數來實現的。 fs.readFile 接收了三個參數,第一個是文件名,第二個是編碼方式,第三個是一個函數,咱們稱這個函數爲回調函數。
JavaScript 支持匿名的函數定義方式,譬如咱們例子中回調函數的定義就是嵌套在fs.readFile 的參數表中的。這種定義方式在 JavaScript 程序中極爲廣泛,與下面這種定義
方式實現的功能是一致的:
//readfilecallback.js
function readFileCallBack(err, data) {
if (err) {
console.error(err);
} else {
console.log(data);
}
}
var fs = require('fs');
fs.readFile('file.txt', 'utf-8', readFileCallBack);
console.log('end.');
fs.readFile 調用時所作的工做只是將異步式 I/O 請求發送給了操做系統,而後當即返回並執行後面的語句,執行完之後進入事件循環監聽事件。當 fs 接收到 I/O 請求完成的事件時,事件循環會主動調用回調函數以完成後續工做。所以咱們會先看到 end. ,再看到file.txt 文件的內容
事件
Node.js 全部的異步 I/O 操做在完成時都會發送一個事件到事件隊列。在開發者看來,事件由 EventEmitter 對象提供。
Node.js 的事件循環機制
Node.js 在何時會進入事件循環呢?答案是 Node.js 程序由事件循環開始,到事件循環結束,全部的邏輯都是事件的回調函數,因此 Node.js 始終在事件循環中,程序入口就是事件循環第一個事件的回調函數。事件的回調函數在執行的過程當中,可能會發出 I/O 請求或直接發射(emit)事件,執行完畢後再返回事件循環,事件循環會檢查事件隊列中有沒有未處理的事件,直到程序結束。
模塊和包
什麼是模塊
模塊是 Node.js 應用程序的基本組成部分,文件和模塊是一一對應的。換言之,一個Node.js 文件就是一個模塊,這個文件多是 JavaScript 代碼、JSON 或者編譯過的 C/C++ 擴展。
在前面章節的例子中,咱們曾經用到了 var http = require('http'), 其中 http是 Node.js 的一個核心模塊,其內部是用 C++ 實現的,外部用 JavaScript 封裝。咱們經過require 函數獲取了這個模塊,而後才能使用其中的對象。
建立及加載模塊
1. 建立模塊
一個文件就是一個模塊
Node.js 提供了 exports 和 require 兩個對象,其中 exports 是模塊公開的接口, require 用於從外部獲取一個模塊的接口,即所獲取模塊的 exports 對象。
例子:
//module.js
var name;
exports.setName = function(thyName) {
name = thyName;
};
exports.sayHello = function() {
console.log('Hello ' + name);
};
在同一目錄下建立 getmodule.js,內容是:
//getmodule.js
var myModule = require('./module');
myModule.setName('BYVoid');
myModule.sayHello();
運行node getmodule.js,結果是:
Hello BYVoid
在以上示例中,module.js 經過 exports 對象把 setName 和 sayHello 做爲模塊的訪問接口,在 getmodule.js 中經過 require('./module') 加載這個模塊,而後就能夠直接訪問 module.js 中 exports 對象的成員函數了。
2. 單次加載
上面這個例子有點相似於建立一個對象,但實際上和對象又有本質的區別,由於
require 不會重複加載模塊,也就是說不管調用多少次 require, 得到的模塊都是同一個。
咱們在 getmodule.js 的基礎上稍做修改:
//loadmodule.js
var hello1 = require('./module');
hello1.setName('BYVoid');
var hello2 = require('./module');
hello2.setName('BYVoid 2');
hello1.sayHello();
運行後發現輸出結果是 Hello BYVoid 2 ,這是由於變量 hello1 和hello2 指向的是同一個實例,所以 hello1.setName 的結果被 hello2.setName 覆蓋,最終輸出結果是由後者決定的。
3. 覆蓋 exports
把一個對象封裝到模塊中,例如:
//singleobject.js
function Hello() {
var name;
this.setName = function (thyName) {
name = thyName;
};
this.sayHello = function () {
console.log('Hello ' + name);
};
};
exports.Hello = Hello;
此時咱們在其餘文件中須要經過 require('./singleobject').Hello 來獲取Hello 對象,這略顯冗餘,能夠用下面方法稍微簡化:
//hello.js
function Hello() {
var name;
this.setName = function (thyName) {
name = thyName;
};
this.sayHello = function () {
console.log('Hello ' + name);
};
};
module.exports = Hello;
//gethello.js
var Hello = require('./hello');
hello = new Hello();
hello.setName('BYVoid');
hello.sayHello()
注意,模塊接口的惟一變化是使用 module.exports = Hello 代替了 exports.Hello=Hello 。在外部引用該模塊時,其接口對象就是要輸出的 Hello 對象自己,而不是原先的exports 。
事實上, exports 自己僅僅是一個普通的空對象,即 {} ,它專門用來聲明接口,本質上是經過它爲模塊閉包的內部創建了一個有限的訪問接口。由於它沒有任何特殊的地方,因此能夠用其餘東西來代替,譬如咱們上面例子中的 Hello 對象。
警告:
不能夠經過對exports 直接賦值代替對module.exports 賦值。
exports 實際上只是一個和 module.exports 指向同一個對象的變量,它自己會在模塊執行結束後釋放,但 module不會,所以只能經過指定module.exports 來改變訪問接口。
建立包
包是在模塊基礎上更深一步的抽象,Node.js 的包相似於 C/C++ 的函數庫或者 Java/.Net的類庫。它將某個獨立的功能封裝起來,用於發佈、更新、依賴管理和版本控制。Node.js 根據 CommonJS 規範實現了包機制,開發了 npm來解決包的發佈和獲取需求。
Node.js 的包是一個目錄,其中包含一個 JSON 格式的包說明文件 package.json。嚴格符合 CommonJS 規範的包應該具有如下特徵:
package.json 必須在包的頂層目錄下;
二進制文件應該在 bin 目錄下;
JavaScript 代碼應該在 lib 目錄下;
文檔應該在 doc 目錄下;
單元測試應該在 test 目錄下。
1. 做爲文件夾的模塊
模塊與文件是一一對應的。文件不只能夠是 JavaScript 代碼或二進制代碼,還能夠是一個文件夾。最簡單的包,就是一個做爲文件夾的模塊。
例子:
創建一個叫作 somepackage 的文件夾,在其中建立 index.js,內容以下:
//somepackage/index.js
exports.hello = function() {
console.log('Hello.');
};
而後在 somepackage 以外創建 getpackage.js,內容以下:
//getpackage.js
var somePackage = require('./somepackage');
somePackage.hello();
運行 node getpackage.js,控制檯將輸出結果 Hello. 。
咱們使用這種方法能夠把文件夾封裝爲一個模塊,即所謂的包。包一般是一些模塊的集合,在模塊的基礎上提供了更高層的抽象,至關於提供了一些固定接口的函數庫。經過定製
package.json,咱們能夠建立更復雜、更完善、更符合規範的包用於發佈。
在前面例子中的 somepackage 文件夾下,咱們建立一個叫作 package.json 的文件,內容如
下所示:
{
"main" : "./lib/interface.js"
}
而後將 index.js 重命名爲 interface.js 並放入 lib 子文件夾下。以一樣的方式再次調用這個包,依然能夠正常使用。
Node.js 在調用某個包時,會首先檢查包中 package.json 文件的 main 字段,將其做爲包的接口模塊,若是 package.json 或 main 字段不存在,會嘗試尋找 index.js 或 index.node 做爲包的接口。
備註:package.json裏面不能有main{}之類的註釋,否則會報錯
package.json是CommonJS 規定的用來描述包的文件,徹底符合規範的 package.json 文件應該含有如下字段。
name :包的名稱,必須是惟一的,由小寫英文字母、數字和下劃線組成,不能包含空格。
description :包的簡要說明。
version :符合語義化版本識別
keywords :關鍵字數組,一般用於搜索。
maintainers :維護者數組,每一個元素要包含 name 、 email (可選)、 web (可選)字段。
contributors :貢獻者數組,格式與 maintainers 相同。包的做者應該是貢獻者數組的第一個元素。
bugs :提交bug的地址,能夠是網址或者電子郵件地址。
licenses :許可證數組,每一個元素要包含 type (許可證的名稱)和 url (連接到許可證文本的地址)字段。
repositories :倉庫託管地址數組,每一個元素要包含 type (倉庫的類型,如 git )、url (倉庫的地址)和 path (相對於倉庫的路徑,可選)字段。
dependencies :包的依賴,一個關聯數組,由包名稱和版本號組成。
例子:
{
"name": "mypackage",
"description": "Sample package for CommonJS. This package demonstrates the required
elements of a CommonJS package.",
"version": "0.7.0",
"keywords": [
"package",
"example"
],
"maintainers": [
{
"name": "Bill Smith",
"email": "bills@example.com",
}
],
"contributors": [
{
"name": "BYVoid",
"web": "http://www.byvoid.com/"
}
],
"bugs": {
"mail": "dev@example.com",
"web": "http://www.example.com/bugs"
},
"licenses": [
{
"type": "GPLv2",
"url": "http://www.example.org/licenses/gpl.html"
}
],
"repositories": [
{
"type": "git",
"url": "http://github.com/BYVoid/mypackage.git"
}
],
"dependencies": {
"webkit": "1.2",
"ssl": {
"gnutls": ["1.0", "2.0"],
"openssl": "0.9.8"
}
}
}
Node.js 包管理器
Node.js包管理器,即npm是 Node.js 官方提供的包管理工具,它已經成了 Node.js 包的標準發佈平臺,用於 Node.js 包的發佈、傳播、依賴控制。npm 提供了命令行工具,使你能夠方便地下載、安裝、升級、刪除包,也可讓你做爲開發者發佈並維護包。
本地模式和全局模式
本地模式:npm [install/i] [package_name]
全局模式:npm [install/i] -g [package_name]
爲何要使用全局模式呢?多數時候並非由於許多程序都有可能用到它,爲了減小多重副本而使用全局模式,而是由於本地模式不會註冊 PATH 環境變量。舉例說明,咱們安裝supervisor 是爲了在命令行中運行它,譬如直接運行 supervisor script.js,這時就須要在 PATH環境變量中註冊 supervisor。npm 本地模式僅僅是把包安裝到 node_modules 子目錄下,其中的 bin 目錄沒有包含在 PATH 環境變量中,不能直接在命令行中調用。而當咱們使用全局模式安裝時,npm 會將包安裝到系統目錄,譬如 /usr/local/lib/node_modules/,同時 package.json 文件中 bin 字段包含的文件會被連接到 /usr/local/bin/。/usr/local/bin/ 是在 PATH 環境變量中默認定義的,所以就能夠直接在命令行中運行 supervisor script.js 命令了。
建立全局連接
npm 提供了一個有趣的命令 npm link,它的功能是在本地包和全局包之間建立符號連接。咱們說過使用全局模式安裝的包不能直接經過require 使用,但經過 npm link 命令能夠打破這一限制。舉個例子,咱們已經經過 npm install -g express 安裝了 express ,這時在工程的目錄下運行命令:
$ npm link express
./node_modules/express -> /usr/local/lib/node_modules/express
咱們能夠在 node_modules 子目錄中發現一個指向安裝到全局的包的符號連接。經過這種方法,咱們就能夠把全局包當本地包來使用了。
包的發佈
*編寫模塊
1)新建文件夾,好比:somepackage
2) 該文件夾下新建js文件,好比:index.js
js內容以下:
exports.sayHello=function(){
return "Hello,zhoudaozhang.";
};
*初始化包描述文件
使用cmd命令定位到somepackage文件夾
輸入 npm init 並執行
npm的init命令能夠幫助你生成package.json文件,這是個人文件內容:
{
"name": "somepackage_xiaotian",
"version": "1.0.0",
"description": "'hehe'",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"Hello",
"world"
],
"author": "zhou daozhang",
"license": "ISC"
}
*註冊包倉庫帳號
npm adduser
輸入這個命令會有提示輸入用戶名,密碼,郵箱等資料
這和去官方源倉庫https://www.npmjs.com/註冊是同樣的
*上傳包
npm publish
若是上傳成功會提示
+somepackage_xiaotian@1.0.0 不然上傳失敗
這個時候去https://www.npmjs.com/登錄倉庫帳號就能夠看到本身的包啦
*安裝包
npm install somepackage_xiaotian
經過此命令能夠在世界上任一一臺機器上安裝somepackage_xiaotian了
發佈包過程可能會遇到不少問題,我印象比較深入的是npm ERR publish 403
You do not have permission to publish 'somepackage'.Are you logged in as
the corrent user?:somepackage
意思是我沒權限發佈somepackage,並問我是否使用了正確的帳號,
那也許是somepackage被別人發佈過了吧,因此我修改了package.json文件
把name改爲somepackage_xiaotian.
*分析包
這個命令能夠爲你分析出當前路徑下可以經過模塊路徑找到的全部包,並生成依賴樹。
npm ls
全局對象Global
JavaScript 中有一個特殊的對象,稱爲全局對象(Global Object),它及其全部屬性均可以在程序的任何地方訪問,即全局變量。在瀏覽器 JavaScript 中,一般 window 是全局對象,而 Node.js 中的全局對象是 global ,全部全局變量(除了 global 自己之外)都是 global
對象的屬性。
全局對象與全局變量
global 最根本的做用是做爲全局變量的宿主
知足如下條件的變量是全局變量:
在最外層定義的變量;
全局對象的屬性;
隱式定義的變量(未定義直接賦值的變量)。
注:在 Node.js 中你不可能在最外層定義變量,由於全部用戶代碼都是屬於當前模塊的,而模塊自己不是最外層上下文
提示:永遠使用 var 定義變量以免引入全局變量,由於全局變量會污染命名空間,提升代碼的耦合風險。
Process
process 是一個全局變量,即 global 對象的屬性。它用於描述當前 Node.js 進程狀態的對象,提供了一個與操做系統的簡單接口
process.argv 是命令行參數數組,第一個元素是 node,第二個元素是腳本文件名
process.stdout 是標準輸出流,一般咱們使用的 console.log() 向標準輸出打印字符,而 process.stdout.write() 函數提供了更底層的接口。
process.stdin 是標準輸入流,初始時它是被暫停的,要想從標準輸入讀取數據,你必須恢復流,並手動編寫流的事件響應函數。
process.nextTick(callback) 的功能是爲事件循環設置一項任務,Node.js 會在下次事件循環調響應時調用 callback 。
http://nodejs.org/api/process.html詳細瞭解的地方
console
console 用於提供控制檯標準輸出
console.log() :向標準輸出流打印字符並以換行符結束
console.error() :與 console.log() 用法相同,只是向標準錯誤流輸出。
console.trace() :向標準錯誤流輸出當前的調用棧。
經常使用工具 util
util 是一個 Node.js 核心模塊,提供經常使用函數的集合,用於彌補核心 JavaScript 的功能過於精簡的不足。
util.inherits
util.inherits(constructor, superConstructor) 是一個實現對象間原型繼承的函數。
var util = require('util');
function Base() {
this.name = 'base';
this.base = 1991;
this.sayHello = function() {
console.log('Hello ' + this.name);
};
}
Base.prototype.showName = function() {
console.log(this.name);
};
function Sub() {
this.name = 'sub';
}
util.inherits(Sub, Base);
var objBase = new Base();
objBase.showName();
objBase.sayHello();
console.log(objBase);
var objSub = new Sub();
objSub.showName();
//objSub.sayHello();
console.log(objSub);
結果:
base
Hello base
{ name: 'base', base: 1991, sayHello: [Function] }
sub
{ name: 'sub' }
這個只能繼承原型裏面的屬性和方法
util.inspect
util.inspect(object,[showHidden],[depth],[colors]) 是一個將任意對象轉換爲字符串的方法,一般用於調試和錯誤輸出。它至少接受一個參數 object ,即要轉換的對象。
showHidden 是一個可選參數,若是值爲 true ,將會輸出更多隱藏信息。
depth 表示最大遞歸的層數,若是對象很複雜,你能夠指定層數以控制輸出信息的多
少。若是不指定 depth ,默認會遞歸2層,指定爲 null 表示將不限遞歸層數完整遍歷對象。
若是 color 值爲 true ,輸出格式將會以 ANSI 顏色編碼,一般用於在終端顯示更漂亮的效果。
注:util.inspect 並不會簡單地直接把對象轉換爲字符串,即便該對
象定義了 toString 方法也不會調用。
module.exports 和exports的區別?
Module.exports纔是真正的接口,exports只不過是它的一個輔助工具。 最終返回給調用的是Module.exports而不是exports。
全部的exports收集到的屬性和方法,都賦值給了Module.exports。固然,這有個前提,就是Module.exports自己不具有任何屬性和方法。若是,Module.exports已經具有一些屬性和方法,那麼exports收集來的信息將被忽略。
這就是 EventEmitter 最簡單的用法。接下來咱們介紹一下 EventEmitter 經常使用的API。
EventEmitter.on(event, listener) 爲指定事件註冊一個監聽器,接受一個字符串 event 和一個回調函數 listener 。
EventEmitter.emit(event, [arg1], [arg2], [...]) 發射 event 事件,傳遞若干可選參數到事件監聽器的參數表。
EventEmitter.once(event, listener) 爲指定事件註冊一個單次監聽器,即監聽器最多隻會觸發一次,觸發後馬上解除該監聽器。
EventEmitter.removeListener(event, listener) 移除指定事件的某個監聽器, listener 必須是該事件已經註冊過的監聽器。
文件系統 fs
fs 模塊是文件操做的封裝,它提供了文件的讀取、寫入、改名、刪除、遍歷目錄、連接等 POSIX 文件系統操做。與其餘模塊不一樣的是, fs 模塊中全部的操做都提供了異步的和同步的兩個版本,例如讀取文件內容的函數有異步的 fs.readFile() 和同步的fs.readFileSync() 。
fs.readFile
fs.readFile(filename,[encoding],[callback(err,data)]) 是最簡單的讀取
文件的函數。它接受一個必選參數 filename ,表示要讀取的文件名。第二個參數 encoding是可選的,表示文件的字符編碼。 callback 是回調函數,用於接收文件的內容。若是不指定 encoding ,則 callback 就是第二個參數。回調函數提供兩個參數 err 和 data , err 表示有沒有錯誤發生, data 是文件內容。若是指定了 encoding , data 是一個解析後的字符串,不然 data 將會是以 Buffer 形式表示的二進制數據。
例;
以 Buffer 形式表示的二進制數據
var fs = require('fs');
fs.readFile('content.txt', function(err, data) {
if (err) {
console.error(err);
} else {
console.log(data);
}
});
encoding 指定編碼
var fs = require('fs');
fs.readFile('content.txt', 'utf-8', function(err, data) {
if (err) {
console.error(err);
} else {
console.log(data);
}
});
fs.readFileSync
fs.readFileSync(filename, [encoding]) 是 fs.readFile 同步的版本。它接受的參數和 fs.readFile 相同,而讀取到的文件內容會以函數返回值的形式返回。若是有錯誤發生, fs 將會拋出異常,你須要使用 try 和 catch 捕捉並處理異常。
Fs.read
fs.read(fd, buffer, offset, length, position, [callback(err, bytesRead,
buffer)]) 是 POSIX read 函數的封裝,相比fs.readFile 提供了更底層的接口。 fs.read的功能是從指定的文件描述符fd 中讀取數據並寫入 buffer 指向的緩衝區對象。offset 是buffer 的寫入偏移量。length 是要從文件中讀取的字節數。position 是文件讀取的起始
位置,若是 position 的值爲 null ,則會從當前文件指針的位置讀取。回調函數傳遞bytesRead 和 buffer ,分別表示讀取的字節數和緩衝區對象。
用的時候,和fs.open一塊兒用
var fs = require('fs');
fs.open('content.txt', 'r', function(err, fd) {
if (err) {
console.error(err);
return;
}
var buf = new Buffer(8);
fs.read(fd, buf, 0, 8, null, function(err, bytesRead, buffer) {
if (err) {
console.error(err);
return;
}
console.log('bytesRead: ' + bytesRead);
console.log(buffer);
})
});
運行結果則是:
bytesRead: 8
<Buffer 54 65 78 74 20 e6 96 87>
HTTP 服務器與客戶端
工程的結構
Express 都生成了哪些文件,除了 package.json,它只產生了兩個 JavaScript 文件 app.js 和 routes/index.js。模板引擎 ejs 也有兩文件 index.ejs 和layout.ejs,此外還有樣式表 style.css。下面來詳細看看這幾個文件。
routes 是一個文件夾形式的本地模塊,即 ./routes/index.js ,它的功能是爲指定路徑組織返回內容,至關於 MVC 架構中的控制器。
routes/index.js 是路由文件,至關於控制器,用於組織展現的內容:
exports.index = function(req, res) {
res.render('index', { title: 'Express' });
};
app.js 中經過 app.get('/', routes.index); 將「 / 」路徑映射到 exports.index函數下。其中只有一個語句 res.render('index', { title: 'Express' }) ,功能是調用模板解析引擎,翻譯名爲 index 的模板,並傳入一個對象做爲參數,這個對象只有一個
屬性,即 title: 'Express' 。
3. index.ejs
index.ejs 是模板文件,即 routes/index.js 中調用的模板,內容是:
<h1><%= title %></h1>
<p>Welcome to <%= title %></p>
它的基礎是 HTML 語言,其中包含了形如<%= title %>的標籤,功能是顯示引用的變量,即 res.render 函數第二個參數傳入的對象的屬性。
4. layout.ejs (新的裏面,沒有這個)
模板文件不是孤立展現的,默認狀況下全部的模板都繼承自 layout.ejs,即<%- body %>
部分纔是獨特的內容,其餘部分是共有的,能夠看做是頁面框架。
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
<%- body %>
</body>
</html>
REST 風格的路由規則
表徵狀態轉移(Representational State Transfer),它是一種基於 HTTP 協議的網絡應用的接口風格,充分利用 HTTP 的方法實現統一風格接口的服務。
安全是指沒有反作用,即請求不會對資源產生變更,連續訪問屢次所得到的結果不受訪問者的影響。而冪等指的是重複請求屢次與一次請求的效果是同樣的,好比獲取和更新操做是冪等的,這與新增不一樣。刪除也是冪等的,即重複刪除一個資源,和刪除一次是同樣的。
在 MVC 架構中,模板引擎包含在服務器端。
基於 JavaScript 的模板引擎有許多種實現,咱們推薦使用 ejs (Embedded JavaScript),由於它十分簡單,並且與 Express 集成良好。因爲它是標準 JavaScript 實現的,所以它不只能夠運行在服務器端,還能夠運行在瀏覽器中。
ejs 的標籤系統很是簡單,它只有如下3種標籤。
<% code %>:JavaScript 代碼。
<%= code %>:顯示替換過 HTML 特殊字符的內容。
<%- code %>:顯示原始 HTML 內容。
視圖助手
Express 提供了一種叫作視圖助手的工具,它的功能是容許在視圖中訪問一個全局的函數或對象,不用每次調用視圖解析的時候單獨傳入。前面提到的 partial 就是一個視圖助手。
因爲ejs的升級,《node.js開發指南》中使用的 partial 函數已經摒棄,使用foreach,include代替
<%- partial('listitem',items) %>
修改成:
1 2 3 4 |
<ul><% items.forEach(function(listitem){%> <% include listitem%> <%}) %> </ul> |
next()
Express 提供了路由控制權轉移的方法,即回調函數的第三個參數 next ,經過調用next() ,會將路由控制權轉移給後面的規則,例如:
app.all('/user/:username', function(req, res, next) {
console.log('all methods captured');
next();
});
app.get('/user/:username', function(req, res) {
res.send('user: ' + req.params.username);
});
當訪問被匹配到的路徑時,如 http://localhost:3000/user/carbo,會發現終端中打印了 allmethods captured ,並且瀏覽器中顯示了 user: carbo 。這說明請求先被第一條路由規則捕獲,完成 console.log 使用 next() 轉移控制權,又被第二條規則捕獲,向瀏覽器返回了信息。
這是一個很是有用的工具,可讓咱們輕易地實現中間件,並且還能提升代碼的複用程度。例如咱們針對一個用戶查詢信息和修改信息的操做,分別對應了 GET 和 PUT 操做,而二者共有的一個步驟是檢查用戶名是否合法,所以能夠經過 next() 方法實現:
var users = {
'byvoid': {
name: 'Carbo',
website: 'http://www.byvoid.com'
}
};
app.all('/user/:username', function(req, res, next) {
// 檢查用戶是否存在
if (users[req.params.username]) {
next();
} else {
next(new Error(req.params.username + ' does not exist.'));
}
});
app.get('/user/:username', function(req, res) {
// 用戶必定存在,直接展現
res.send(JSON.stringify(users[req.params.username]));
});
app.put('/user/:username', function(req, res) {
// 修改用戶信息
res.send('Done');
});
上面例子中, app.all 定義的這個路由規則實際上起到了中間件的做用,把類似請求
的相同部分提取出來,有利於代碼維護其餘 next 方法若是接受了參數,即表明發生了錯誤。
使用這種方法能夠把錯誤檢查分段化,下降代碼耦合度。
微博網站過程當中發現的一些問題;
一、 css和javascritp的路徑不用寫絕對路徑,例:
<script src="/javascripts/jquery-1.11.3.js"></script>
<script src="/javascripts/bootstrap.js"></script>
<link rel='stylesheet' href='/stylesheets/bootstrap.css' />
<link href="/stylesheets/bootstrap-responsive.css" rel="stylesheet">
二、 app.js裏面要加這個
三、 var partials = require('express-partials');
app.use(partials());
以前要安裝express-partials模塊,npm install express-partials –g
3上面這個要寫在
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
後面
數據庫的格式是由表(table)、行(row)、字段(field)組成的。表有固定的結構,規定了
每行有哪些字段,在建立時被定義,以後修改很困難。行的格式是相同的,由若干個固定的字段組成。每一個表可能有若干個字段做爲索引(index),這其中有的是主鍵(primary key),用於約束表中的數據,還有惟一鍵(unique key),確保字段中不存放重複數據。表和表之間
可能還有相互的約束,稱爲外鍵(foreign key)。對數據庫的每次查詢都要以行爲單位,複雜的查詢包括嵌套查詢、鏈接查詢和交叉表查詢。
擁有這些功能的數據庫被稱爲關係型數據庫,關係型數據庫一般使用一種叫作 SQL(Structured Query Language)的查詢語言做爲接口,所以又稱爲 SQL 數據庫。典型的 SQL 數據庫有 MySQL、Oracle、Microsoft SQL Server、PostgreSQL、SQLite,等等。
會話
會話是一種持久的網絡協議,用於完成服務器和客戶端之間的一些交互行爲。會話是一個比鏈接粒度更大的概念,一次會話可能包含屢次鏈接,每次鏈接都被認爲是會話的一次操做。在網絡應用開發中,有
必要實現會話以幫助用戶交互。例如網上購物的場景,用戶瀏覽了多個頁面,購買了一些物品,這些請求在屢次鏈接中完成。許多應用層網絡協議都是由會話支持的,如 FTP、Telnet 等,而 HTTP 協議是無狀態的,自己不支持會話,所以在沒有額外手段的幫助下,前面場景中服務器不知道用戶購買了什麼。
爲了在無狀態的 HTTP 協議之上實現會話,Cookie 誕生了。Cookie 是一些存儲在客戶端的信息,每次鏈接的時候由瀏覽器向服務器遞交,服務器也向瀏覽器發起存儲 Cookie 的請求,依靠這樣的手段服務器能夠識別客戶端。咱們一般意義上的 HTTP 會話功能就是這樣
實現的。具體來講,瀏覽器首次向服務器發起請求時,服務器生成一個惟一標識符併發送給客戶端瀏覽器,瀏覽器將這個惟一標識符存儲在 Cookie 中,之後每次再發起請求,客戶端瀏覽器都會向服務器傳送這個惟一標識符,服務器經過這個惟一標識符來識別用戶。
Underscore模塊