nodejs學習筆記

Hello Fuck以下:javascript

console.log('Hello, Fuck you, NodeJs');

# node Helloworld.js
Hello, Fuck you, NodeJs

事件:html

Node.js全部的異步i/o操做在完成時都會發送一個事件到事件隊列,事件由EventEmitter對象來提供,前面提到的fs.readFile和http.createServer的回調函數都是經過EventEmitter來實現的。java

//event.js
var EventEmitter = require('events').EventEmitter;
var event = new EventEmitter();

event.on('some_event', function() {
    console.log('some_event occurred.');
    setTimeout(function(){
        event.emit('some_event');
    },1000);
});

setTimeout(function() {
    event.emit('some_event');
},1000);

console.log('end?');

# node event.js 
end?
some_event occurred.
some_event occurred.
some_event occurred.
^C

 nodejs不停監測是否有活動的事件監聽器好比i/o, timer等,一旦發現沒有活動的事件監聽器,nodejs進程將退出。node

 

模塊c++

模塊是Node.js的基本組成部分,文件和模塊是一一對應的,換言之,一個Node.js文件就是一個模塊,這個文件多是JavaScript代碼,JSON或者編譯過的c/c++擴展。git

前面章節的例子中,咱們曾經用到了相似於var http=require('http'),其中http就是一個核心模塊,其內部是用c++來實現的,外部使用javascript來進行封裝。經過require函數獲取了這個模塊以後,而後才能使用其中的對象。github

建立,加載模塊web

在Node.js中,建立一個模塊很是單簡單,由於一個文件就是一個模塊,咱們關注的問題僅僅在於如何在其它文件中獲取這個模塊,Node.js提供了exports和require兩個對象,其中exports是模塊公開的接口,而require用於從外部獲取一個模塊的接口,也就是獲取返回的exports對象。express

// module.js
var name;
exports.setName=function(theName) {
    name=theName;
}

exports.sayHello=function() {
    console.log('Fuck you ' + name);
}


// getModule.js
var myModule=require('./module') //注意這裏須要./前綴,由於是相對當前工做目錄的。
myModule.setName('Mosmith');
myModule.sayHello();


# node getModule.js
Hello Mosmith

 

單次加載,這個有點相似於建立一個對象,但實際上和對象又有本質的區別,由於require不會重複加載模塊,也就是說不管調用多少次require,得到的模塊都是同一個。npm

覆蓋exports,有時候咱們只是想將一個對象封閉到一個模塊中,例如:

// module.js
function Hello() {
    var name;
    this.setName=function(_name) {
        this.name=_name;
    }
    this.sayHello=function() {
        console.log("Hello " + this.name);
    }
}
exports.Hello=Hello;

// override exports object
//module.exports=Hello;


// getModule.js
var Hello = require('./module.js').Hello;
var hello=new Hello();
hello.setName('Mosmith');
hello.sayHello();

須要注意的是,不能夠經過對 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的包應該具有如下特徵。

  1. package.json必須在包的頂層目錄下
  2. 二進制文件應該在bin目錄下
  3. JavaScript代碼應該在lib目錄下
  4. 文檔應該在doc目錄下
  5. 單元測試應該在test目錄下

但Node.js對包的要求沒有這麼嚴格,只要頂層目錄下面有package.json,並符合一些規範便可。但最好是符合規範。Node.js在調用某個包是,先會去檢查包中的package.json中的main字段,將其做爲包的接口模塊,若是package.json或者main字段不存在,那麼會嘗試將尋找index.js或者index.node做爲包的接口。

package.json是CommonJS規定用於描述包的文件,完整的包應該含有如下字段:

name:包的名稱,必須是惟一的由小寫字母,數字,和下劃線組成,不能有空格。

description:包的簡要說明。

version:符合語義化版本識別規範的字符串

maintainer:維護者數組,第個元素要包含name, email(可選), web(可選)

contributors:貢獻者數組,格式與maintainer相同,包的做者應該是都數組的第一個元素。

bugs提交bug的地址。

licenses許可證數組,每一個元素要包含type的url

repositories倉庫託管地址數組,每一個元素要包含type(倉庫的類型好比git),url(倉庫的地址)和path(相對於倉庫的路徑,可選)字段。

dependencies:包的依賴,一個關聯數組,由包的名稱和版本號組成。

下面是一下符合CommonJS規範的package.json示例:

{
    "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"
        }
}
}

 

npm是Node.js的包管理工具,它已經成爲Node.js包的標準發佈平臺。用於Node.js包的發佈,傳播,依賴控制。

npm install/i package_name好比要安裝express

npm install express或者npm i express

在項目目錄下運行npm install 將下載並安裝package.json中的依賴。

同時npm還會自動解析其依賴,並獲取express依賴的mime,mkdirp,qs的connect等。

注意:express4.x中將命令工具分離出來了,全部須要先裝express-generator

在新建一個nodejs工程的時候咱們可使用npm init命令來進行初始化package.json,它的功能相似於maven archetype:generate

 

本地模塊與全局模式

默認狀況下npm會從http://npmjs.org搜索並下載包,並將包安裝到當前目前的node_modules子目錄下面。也就是本地模式。

另外npm能夠以全局模式安裝(使用-g參數),使用方法爲:

npm install/i -g packageName

但須要注意的是,全局模式下可能會形成衝突,由於別的nodejs程序可能須要另外版本的包。

 

本地模式下npm不會註冊環境變量,而全局模式下會注意環境變量,並將包安裝到系統目錄好比/usr/local/lib/node_modules,同時package.json文件中的bin字段包含的文件會被連接到/usr/local/bin。/usr/local/bin是在PATH環境變量中默認定義的,所以就能夠直接在命令中運行像supervisor的模塊了。但全局模式安裝的package不能經過require來使用,由於require不會去搜索/usr/local/lib/node_modules目錄。

 

建立全局連接

npm提供了一個npm link命令,它的功能是在本地包和全局包之間建立符號連接,咱們說過使用全局模式安裝的包不能經過require來使用,但經過npm link命令能夠繞過這個限制。好比:

npm link express

這裏能夠在node_modules子目錄發現一個安裝到全局的包的符號連接。但這個命令不能在windows下來使用。

 

調試。

node debug debug.js

node --debug[=port] script.js 而後在另外一個終端node debug localhost:debug_port或者使用IDE來進行遠程調試。

 

全局對象

JavaScript中有一個特殊的對象,稱爲全局對象,它全部的屬性能夠在程序的任何地方訪問,也就是說全局變量,在瀏覽器的JavaScript中,一般window是全局對象,而Node.js中的全局對象是global,全部的全局變量(除了global自己之外),都是global對象的屬性。咱們在Node.js中可以直接訪問到對象經過都是global的屬性好比console, process等。

 

全局對象與全局變量

global最根本的做用是做爲全局變量的宿主,按照ECMAScript的定義,知足如下條件的是全局變量。

  1. 最外層定義的變量。
  2. 全局對象的屬性。
  3. 隱式定義的變量(未定義直接同賦值的變量)

當你定義一個全局變量時,這個變量同時也會成爲全局對象的屬性,反之亦然。須要注意的是,在 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。Node.js 適合 I/O 密集型的應用,而不是計算密集型的應用,由於一個 Node.js 進程只有一個線程,所以在任什麼時候刻都只有一個事件在執行。若是這個事件佔用大量的 CPU 時間,執行事件循環中的下一個事件就須要等待好久,所以 Node.js 的一個編程原則就是儘可能縮短每一個事件的執行時間。 process.nextTick() 提供了一個這樣的工具,能夠把複雜的工做拆散,變成一個個較小的事件。

process.stdin.on('data', function(data) {
    process.nextTick(function() {
        process.stdout.write('do something very time-consuming');
        process.stdout.write(data);
    });
});

須要setTimeout也能夠達到相似的做用,但setTimeout效率很低,回調不能被及時執行。

除了上面幾個比較經常使用的成員,除些以後有process.platform,process.pid,process.execPath,process.memoryUsage(),以及POSIX進程信號響應機制。

 

console

console用於提供控制檯標準/錯誤輸出:

console.log()向標準輸出流打印字符並以換行符結束。console.log接受若干個參數,若是隻有一個參數,則輸出這個參數的字符串形式,若是有多個參數,則相似於c語言的printf命令的格式化輸出。

console.log("Helloworld"); => Helloworld
console.log("Helloworld%s"); => Helloworld%s console.log("Helloworld %s", Mosmith); => Helloworld Mosmith

console.error()與console.log相同,只不過console.error()向標準錯誤流輸出。

console.trace()用於向標準錯誤流輸出當前的調用棧。

 

經常使用工具util

util是Node.js的核心模塊,提供經常使用函數的集合,用於彌補核心JavaScript的功能過於精簡的不足。

util.inherits(constructor, superConstructor)是一個實現對象間原型繼承的函數,JavaScript的面向對象我是基於原型的,與常見的基於類不一樣,JavaScript並無提供對象繼承的語言級別特性,而是經過原型複製來實現的,具體細節咱們在附錄A中說明,這裏咱們只介紹util.inerits的用法,示例以下:

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();
// This is undefined in Sub
// objSub.sayHello();
console.log(objSub);

 

util.inspect

util.inspect(object, [showHidden],[depth],[colors])是一個將任意對象轉換爲字符串的方法,一般用於調試和錯誤輸出,它至少一個參數object,即要轉換的對象,showHidden是一個可選的參數,若是值爲true,將會輸出更多的參數,depth表示最大的遞歸層數,若是對象很複雜,你能夠指定層數以控制輸出信息的多少,默認狀況下遞歸兩層,爲null的狀況下則不限層數。

 

事件驅動events

events是Node.js的最重要的模塊,Node.js自己就是依賴於event實現事件驅動的,而它提供了惟一的接口。events模塊不只僅用於用戶代碼與Node.js下層事件循環的交互,還幾乎被全部的模塊依賴。

 

事件發射器

events模塊只提供了一個對象,events.EventEmitter, EventEmitter的核心就是事件發射與事件監聽器功能的封裝。EventEmitter的每個事件由一個事件和若干個參數組成,事件名是一個字符串,一般表達必定的語義,對於每一個事件,EventEmitter支持若干個事件監聽器,當事件發射時,註冊到這個事件的事件監聽器將被依次調用,事件參數做爲回調函數傳遞。

// eventEmitterTest.js
var
events=require('events'); var emitter=new events.EventEmitter(); emitter.on('someEvent',function(arg1,arg2){ console.log('listener1 invoked',arg1,arg2); }); emitter.on('someEvent', function(arg1,arg2) { console.log('listener2 invoked',arg1,arg2); }); emitter.emit('someEvent','argument1',2017); $ node eventEmitterTest.js listener1 invoked argument1 2017 listener2 invoked argument1 2017
  • EventEmitter.on(event, listener) 爲指定事件註冊一個監聽器,接受一個字符串 event 和一個回調函數 listener 。
  • EventEmitter.emit(event, [arg1], [arg2], [...]) 發射event事件,傳遞若干可選參數到事件監聽器的參數表。
  • EventEmitter.once(event, listener) 爲指定事件註冊一個單次監聽器,即監聽器最多隻會觸發一次,觸發後馬上解除該監聽器。
  • EventEmitter.removeListener(event, listener) 移除指定事件的某個監聽器, listener 必須是該事件已經註冊過的監聽器。
  • EventEmitter.removeAllListeners([event]) 移除全部事件的全部監聽器,若是指定 event ,則移除指定事件的全部監聽器

error事件

EventEmitter定義了一個特殊的事件error,它包含了’錯誤‘的語義,咱們在遇到異常的時候一般會發射error事件。當error被髮射時,EventEmitter規定若是沒有響應的監聽器,那麼Node.js會將它看成異常,退出程序並打印調用棧。所以咱們通常要爲會發射error事件的對象設置監聽器,避免遇到錯誤後整個程序崩潰。好比:

var events = require('events');
var emitter = new events.EventEmitter();
emitter.emit('error');

運行時會顯示如下錯誤:
node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
Error: Uncaught, unspecified 'error' event.
at EventEmitter.emit (events.js:50:15)
at Object.<anonymous> (/home/byvoid/error.js:5:9)
at Module._compile (module.js:441:26)
at Object..js (module.js:459:10)
at Module.load (module.js:348:31)
at Function._load (module.js:308:12)
at Array.0 (module.js:479:10)
at EventEmitter._tickCallback (node.js:192:40)

 

繼承EventEmitter

大多數時候咱們不會直接使用 EventEmitter ,而是在對象中繼承它。包括 fs 、 net 、http 在內的,只要是支持事件響應的核心模塊都是 EventEmitter 的子類。爲何要這樣作呢?緣由有兩點。首先,具備某個實體功能的對象實現事件符合語義,事件的監聽和發射應該是一個對象的方法。其次 JavaScript 的對象機制是基於原型的,支持部分多重繼承,繼承 EventEmitter 不會打亂對象原有的繼承關係。

 

文件系統fs

fs模塊是文件操做的封閉,它提供了文件的讀取,寫入,改名,刪除,遍歷目錄,連接等POSIX文件操做,與其它模塊不一樣的是,fs模塊全部的操做都有同步和異步兩個版本,例如讀取文件內容的函數有異步的fs.readFile()也有同步的fs.readFileSync

fs.readFile(filename, [encoding], [callback(err,data)])是最簡單的讀取文件的函數,它接受一個必選的參數filename,若是不提供encoding,那麼將以二進制方式打開,若是指定了encoding,data是一個解析後的字符串。

fs.readFIleSync是同步版本,它沒有callback參數,data經過返回值獲取,而錯誤則須要經過try catch來進行捕捉處理。

fs.open(path, flag, [mode], [callback(err,fd])]是POSIX open函數的封裝,與c和fopen相似,它兩個必選參數,path爲文件路徑,flag是表示以什麼方式打開文件,mode參數用於建立文件時給文件指定權限,默認是0666,回調函數將傳遞一個錯誤參數,以及一個文件描述符fd

fs.read(fd,buffer, offset, length, position, callback(err, byteRead, buffer)]是POSIX read函數的封閉,相比於fs.readFile, 它提供了更加底層的接口,fs.read功能是從指定的文件描述符fd中讀取數據並寫入buffer指定的緩衝區對象,offset是buffer的寫入偏移量,length是要從文件中讀取的字節數,position是文件讀取的起始位置,若是position的值爲null,則會從當前文件指針的位置讀取,回調函數傳遞byteRead和buffer,分別表示讀取的字節數和緩衝區對象。

其中有兩個全局變量:

__dirname:全局變量,存儲的是文件所在的文件目錄
__filename:全局變量,存儲的是文件名

 

http模塊

Node.js標準庫裏面提供了http模塊,其中Node.js 標準庫提供了 http 模塊,其中封裝了一個高效的 HTTP 服務器和一個簡易的HTTP 客戶端。 http.Server 是一個基於事件的 HTTP 服務器,它的核心由 Node.js 下層 C++部分實現,而接口由 JavaScript 封裝,兼顧了高性能與簡易性。 http.request 則是一個HTTP 客戶端工具,用於向 HTTP 服務器發起請求,例如實現 Pingback 1 或者內容抓取

 

http.Server

http.Server是http模塊中的HTTP服務器對象,用Node.js作的全部基於HTTP協議的系統如網站,社交應用,甚於代理服務器,都是基於http.Server來實現的,它提供了一套封裝級別很低的API,僅僅是流控制和簡單的消息解析,全部的高層功能都要經過它的接口來實現。

相關文章
相關標籤/搜索