第三章 異步式I/O與事件編程
3.1 npm install -g supervisor
supervisor app.js 當後臺修改代碼後,服務器自動重啓,生效修改的代碼,不用手動中止/啓動
3.2 單線程異步I/O
減小了多線程的開銷,對於操做系統,建立線程的開銷很大,需分配內存、列入調度。同時線程切換時
須要內存換頁,CPU的緩存被清空,切換回來時,須要從新從內存中讀取信息,破壞了數據的局部性。
異步讀取文件:javascript
var fs = require('fs') fs.readFile('test.txt','utf-8',function(err,data){ if(err){ console.log(err); } else console.log(data); }) console.log('end'); 輸出: end; content of the test.txt
3.3模塊和包
3.3.一、模塊
什麼是模塊:
一個文件就是一個模塊。
建立模塊:
exports和require實現
舉例說明java
//module.js var name; exports.setName = function(thyname){ name = thyname; } exports.getName = function(){ console.log(name); } //getnodule.js var module = require('./module') module.setName('xxx') module.getName(); 對於對象的封裝: //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') var hello = new Hello(); hello.setName("xxx"); hello.sayHello();
3.3.二、包
將一個文件夾封裝成一個模塊,即所謂的包
(1)做爲文件夾的模板
一、創建一個somepackage文件夾
二、在somepackage下創建index.js文件 (必須是index.js)
三、文件中寫入node
exports.hello=function(){ console.log('Hello World'); }; 在somepackage文件夾外創建一個getpackage.js文件 寫入 var somepackage=require('./somepackage'); somepackage.hello(); 3.2.2 package.json 在主目錄下建立package.json,寫入: { "main":"./lib/interface.js" } 在主目錄中創建lib子文件夾,並在其中建立interface.js文件,在文件中寫入 exports.hello=function(){ console.log('Hello Node.js'); }; 運行上面的getpackage.js獲得相同的結果。
Node.js在調用某個包時,會首先檢查包中package.json中的main字段,將其做爲包的接口模塊,若是main不存在,會嘗試尋找index.js或者index.node做爲包的接口
3.4nodejs的包管理器npm
npm install [包名]
npm install -g [包名]//全局目錄安裝
建立的全局模式的包,不能直接require使用,須要建立全局連接
npm link [包名]
./node_moudles/[包名] -> /usrt/local/lib/node_moudles/expresslinux
3.5npm包的發佈
一、npm init 填寫好package.json信息
二、npm adduser 按照提示填寫帳號信息,用於之後維護包
三、npm publish 發佈包
打開瀏覽器 http://npmjs.org/能夠查找到,並且在世界商任意一臺主機上,均可以用npm install [包名]來安裝你發佈的包
3.6調試
3.6.1命令行調試
在設置斷點的代碼處添加debugger
node debug app.js
進入調試界面,help查看調試命令
3.6.2遠程調試
//在一個終端裏(不加port,默認是5858)
node --debug-brk[=port] app.js
或 node --debug[=port] app.js
//在另外一個終端中
node debug 127.0.0.1:5858或 node debug 127.0.0.1:[port]
3.6.3node-inspector調試
npm install -g node-inspector
//運行腳本
node --debug-brk=5858 app.js
//啓動node-inspector
$ node-inspector
//打開瀏覽器
http://127.0.0.1:8080/debug?port=5858express
第四章nodejs核心模塊
4.1全局對象
永遠使用var定義變量,避免引入全局變量,由於你全局變量會污染明明空間。
4.1.1 process.argv命令行參數數組
第一個參數是node 第二個參數是腳本文件名,第三個參數是運行參數
4.1.2 process.nextTick(callback)npm
爲事件循環設置一項任務,將複雜的任務,拆分紅一個個較小的事件編程
舉例說明: function doSomething(callback){ compile() callback() } doSomething(function onEnd){ compute() } 改進後 function doSomething(callback){ compile() process.nextTick(callback) } doSomething(function onEnd){ compute() } 改進後的程序會把上面耗時的操做拆分位兩個事件,減小每一個事件的執行時間,提升時間響應效率。
4.2經常使用工具util
4.2.1 util.inspect將人已對象轉換成字符串。
util.inspect(object.[showHidden],[depth],[color])
showHidden可選參數,若是是true,輸出更過隱藏信息
depth可選參數,最大遞歸層數,默認2,指定爲null表示不想遞歸層數,完整遍歷。
color可選參數,true表示輸出格式以ANSI顏色編碼,終端顯示更漂亮。
4.3事件驅動events(最重要的模塊)
4.3.1事件發射器json
events.EventEmitter 時間發射和時間監聽 var events = require('events') var emitter = new events.EventEmitter() emitter.on('someEvent',function(arg1,arg2){ console.log('listen ',arg1,arg2) }) emitter.emit('someEvent','byvoid',1991) 輸出 listen byvoid 1991
4.4文件系統fs
4.4.1 fs.readFile()異步讀取文件數組
fs.readFile('filename','utf-8',function(err,data){ if(err){ console.log(err) } else{ console.log(data) } })
4.4.2 fs.readFileSync(filename,[encoding])同步讀取文件
4.4.3 fs.open(path,flags,[mode],[callback(err,fd)])
path:文件路徑, flags:r,r+,w,w+,a,a+ 兩個必選參數
mode:用於建立文件時給文件的指定權限,默認是0666
4.4.4 fs的其餘經常使用函數:
1)讀取文件
異步:fs.readFile(filename,[encoding],[callback(err,data)])
同步:fs.readFileSync(filename,[encoding])
2)寫入文件(清空原有的)
異步:fs.writeFile(filename,data,[encoding],[callback(err)])
同步:fs.writeFileSync(filename,data,[encoding])
3)追加寫入文件
異步:fs.appendFile(filename,data,[encoding],[callback(err)])
同步:fs.appendFileSync(filename,data,[encoding])
4)刪除文件
異步fs.unlink(path,[callbacl(err)])
同步:fs.unlinkSync(path)
5)建立目錄
異步:fs.mkdir(path,[mode],[callback(err)])
同步:fs.mkdirSync(path,[mode])
6)刪除目錄
異步:fs.rmdir(path,[callback(err)])
同步:fs.rmdirSync(path)
7)讀取目錄(讀取目錄下的全部文件,輸出一個數組)
異步:fs.readdir(path,[callback(err)])
同步:fs.readdirSync(path)
8)獲取文件真實路徑
異步:fs.realpath(filename,[callback(err,files)])
同步:fs.realpathSync(filename)
9)改名
異步:fs.rename(path1,path2,[callback(err)])
同步:fs.renameSync(path1,path2)
10)更改權限
異步:fs.chmod(path,mode,[callback(err)])
同步:fs.chmodSync(path,mode)
11)更改全部權
異步:fs.chown(path,uid,gid,[callback(err)])//uid和gid爲整形,linux下運行id命令能夠查詢
同步:fs.chownSync(path,uid,gid)瀏覽器
第六章 Nodejs進階話題
6.1 加載緩存
nodejs不會重複加載,這是由於nodejs經過文件名緩存全部加載過的文件模塊,因此之後再放問到時,就不會重複加載了。require後只需加載一次
6.2 Nodejs應用部署
6.2.1 不支持故障恢復
當程序發生錯誤時,後臺直接垮掉
6.2.2 沒有日誌輸出
解決方法:Express支持另種運行模式:開發模式和產品模式。前者用於調試,後者用於部署。
只需設置NODE_ENV變量便可,NODE_ENV = production實現產品模式,node app.js能夠看到
Express server listening on port 3000 in production
在app.js的最上方添加一下代碼,實現日誌打印
var fs = require('fs'); var accessLogfile = fs.createWriteStream('access.log',{flags:'a'}); var errorLogfile = fs.createWriteStream('error.log',{flags:'a'}); app.use(express.logger({stream: accessLogfile})) //對於錯誤日誌,須要單獨實現錯誤響應 app.config('production',function(){ app.error(function(err,req,res,next){ var meta = '['+ new Date() +']' + req.url+'\n'; errorLogfile.write(meta+err.stack+'\n'); next(); }) })
6.2.3 沒法利用多核提升性能(單線程)
解決方法:cluster模塊。cluster的功能是生成與當前進程相同的子進程,並容許子進程和父進程之間共享端口。
調用cluster模塊,將主進程生成若干工做進程。
6.2.4 獨佔端口
解決方法:利用ngix,經過反向代理實現nodejs虛擬主機
下面舉例配置文件
server { listen 80; server_name mysite.com; location /{ proxy_pass http://localhost:3000; } }
該配置文件的功能是,監聽訪問mysite.com 80端口的請求,並將多有的請求轉發給localhost:3000
6.2.5 須要手動啓動(重啓服務器的化)
解決方法:編寫開機腳本
6.3 nodejs不適合作什麼
6.3.1 不適合計算密集型程序
6.3.2 不適合單用戶多任務應用。
node適合解決大量併發請求,單用戶不存在併發請求。
6.3.3 不適合邏輯十分複雜的事物
node更善於處理邏輯簡單但訪問頻繁的任務。
附錄A JavaScript的高級特性
A-一、做用域
js的做用域是經過函數來定義的。變量的搜索由函數內向函數外擴展
var scop = "global" var f = function(){ console.log(scop);//輸出undefined scop = "f"; }
注:js訪問未定義或着定義了但未初始化的變量,都是undefined
A-二、對象
js只有對象,對象就是對象,不是類的實例
js中的對象不是基於類實現的,而是基於原型實現的。
一、var foo = {} //即爲對象
foo.prop_1 = 'bar'
二、使用對象初始化器建立對象
var foo = { prop1:'bar', prop2:function(){ } }
三、使用構造函數建立對象
function User(name, uri){ this.name = name; this.uri = uri; this.display = function(){ return this.name; } } var someuser = new User('name','http://baidu.com')
四、上下文對象(call)
call能夠實現繼承。
user = { name:"xxxx", uri:'baudu.com', display: function(words){ console.log(this.name+" "+words) } } var foo = { name:'foobar' } user.display.call(foo,'hello') 輸出: foobar hello
A-三、原型
利用原型來初始化對象
function Foo(){} Foo.prototype.name = "user" Foo..prototype.getName = function() { return this.name }; var foo = new Foo() foo.getName()//輸出user
舉例說明構造函數內建立屬性和原型定義成員函數的區別
function Foo(){ this.prop = "prop"; this.getProp = function(){ return this.prop } } Foo.prototype.name = "user" Foo..prototype.getName = function() { return this.name }; var foo1 = new Foo() var foo2 = new Foo() console.log(foo1.getProp ==foo2.getProp)//false console.log(foo1.getName ==foo2.getName)//true
構造函數內定義的任何屬性,包括函數在內都被重複構建,同一個構造函數產生的兩個對象不共享實例。
因此儘可能用原型定義成員函數,減小開銷。
儘可能在構造函數內定義通常成員,尤爲是數組或對象,由於原型中定義的成員是多個實例共享的。
B、Nodejs編程規範
一、兩個空格作縮進
二、分號作換行符
三、var定義變量 絕對不能使用賦值隱式定義的變量(全局變量,空間污染),確保每一個語句定義一個變量,(不能定義多個變量,之間用','分割)
四、變量名和屬性名(駝峯式命名空間)
五、對於函數定義,() {}之間要有一個空格
六、贊成使用單引號
七、等號儘可能使用===
八、命名函數
回調函數和構造函數,儘可能給函數命名。
對於回調函數,Nodejs和第三方模塊約定第一個參數是err
九、對象定義 全部成員函數,儘可能用原型進行定義,屬性在構造函數內定義,而後對構造函數用new進行對象的建立