三天node入門(day1)

1.前言

目錄:

  1. 安裝(就不說了,網(wǎng)上去找)
  2. 模塊
  3. 代碼的組織和部署
  4. 文件操作

本文的命令行為

$ node node_test.js test.py test.py1

process.argv[2]test.py

有疑問或是文章中有錯(cuò)誤的地方,請(qǐng)?jiān)谠u(píng)論區(qū)指出來,如果私信的話別人就看不到了,加油一起進(jìn)步~~(゜▽゜*)?

2.模塊

每一個(gè)文件就是一個(gè)模塊,文件的路徑就是模塊名。在每個(gè)模塊中,都有requireexportsmodule這三個(gè)變量可以使用。

一個(gè)模塊的代碼只在初始化時(shí)執(zhí)行一次,之后被緩存以供重復(fù)調(diào)用。

a.require

一個(gè)方法,用于在一個(gè)模塊中,加載另一個(gè)模塊的,返回一個(gè)導(dǎo)出對(duì)象。建議傳入相對(duì)路徑而不是絕對(duì)路徑。

const foo1 = require('./foo') // .js文件擴(kuò)展名可以忽略
const data = require('./data.json') //也可以加載json文件

b.exports

一個(gè)對(duì)象,是當(dāng)前模塊的導(dǎo)出對(duì)象,用于導(dǎo)出方法或?qū)傩裕渌K使用require方法可以調(diào)用當(dāng)前模塊的導(dǎo)出對(duì)象。

exports.sayName=function(name){
    console.log('Hi' +name)
}

c.module

一個(gè)對(duì)象,可以訪問當(dāng)前模塊的相關(guān)信息,點(diǎn)擊查看他的相關(guān)屬性。最多的用途是用來替換當(dāng)前模塊的導(dǎo)出模塊,他的默認(rèn)導(dǎo)出值為空對(duì)象,這時(shí)我們來將他改成一個(gè)函數(shù)。

module.exports = function () {
    console.log('test');
};

d.主模塊

node只有一個(gè)入口文件稱為主模塊,需要調(diào)用命令行來啟用的。

node main.js

在一個(gè)文件中多次調(diào)用相同模塊時(shí),被引入的模塊的內(nèi)部變量只初始化一次,不會(huì)開辟新的內(nèi)存。

3.代碼的組織和部署

a.模塊的路徑解析規(guī)則

從上一章我們知道了require支持相對(duì)路徑和絕對(duì)路徑。但這種引入方式在后期維護(hù)上如果修改了某個(gè)文件的存放位置,在引用它的文件中也要隨之修改相關(guān)路徑,牽一發(fā)而動(dòng)全身。require還有第三種的路徑寫法。

內(nèi)置模塊

內(nèi)置模塊沒有路徑解析,直接返回模塊的導(dǎo)出對(duì)象

const fs=require('fs')

node_modules

大部分第三方模塊都安裝于此目錄下,在引入時(shí)直接忽略node_modules文件夾之前的路徑,直接引入就好。

比如該路徑為:

/mytac/node_modules/app

引入時(shí)

const app=require('app')

NODE_PATH環(huán)境變量

定義NODE_PATH環(huán)境變量,如

NODE_PATH=/a/b/c

當(dāng)引用require('file')時(shí),node會(huì)嘗試以下路徑

/a/b/c/file

b.包

由多個(gè)子模塊組成的模塊稱為包,把所有子模塊放在同個(gè)目錄下,寫過npm包的人會(huì)知道,這個(gè)大模塊需要一個(gè)入口文件。如果入口文件名為index的話,在引入時(shí)直接寫之前的路徑就好了,如:

const app=require('application')
// 等價(jià)于
const app=require('application/index')

c.命令行

舉個(gè)栗子,希望有一個(gè)命令行程序,傳入相關(guān)參數(shù),并將他打印出來

$ node myapp/src/util/node-echo.js Hello world
Hello world

這種使用方法不太像是一個(gè)命令行程序,下面才是我們期望的方式

$ node-echo Hello world

Linux

在Linux下,我們可以把js文件當(dāng)作shell腳本來執(zhí)行,為了達(dá)到上述效果,步驟:

  1. 在shell腳本中,通過#!注釋指定當(dāng)前腳本的解釋器,首先在node-echo.js文件頂部增加這條注釋,證明該腳本需要node來解析。
#! /myapp/src/util/env node
  1. 賦予node-echo.js文件執(zhí)行權(quán)限
$ chmod +x /myapp/src/util/node-echo.js
  1. 在PATH環(huán)境變量下指定某個(gè)目錄,比如要在/myapp/src/util/下創(chuàng)建一個(gè)軟鏈文件,文件名與我們希望使用的終端命令同名,命令如下:
$ sudo ln -s /myapp/src/util/node-echo.js /myapp/src/util/node-echo

這樣處理過后,可以在任意目錄下使用node-echo命令咯~

Windows

windows下與Linux完全不同,需要.cmd文件來解決問題。假如node-echo.js存放在C:\myapp\src\util目錄,并且該目錄已經(jīng)添加到PATH環(huán)境變量里,接下來需要在該目錄下新建一個(gè)名為node-echo.cmd文件,如下:

@node "C:\myapp\src\util\node-echo.js" %*

這樣處理過后,可以在任意目錄下使用node-echo命令咯~

工程目錄標(biāo)準(zhǔn)樣例

- /home/user/workspace/node-echo/   # 工程目錄
    - bin/                          # 存放命令行相關(guān)代碼
        node-echo
    + doc/                          # 存放文檔
    - lib/                          # 存放API相關(guān)代碼
        echo.js
    - node_modules/                 # 存放三方包
        + argv/
    + tests/                        # 存放測(cè)試用例
    package.json                    # 元數(shù)據(jù)文件
    README.md                       # 說明文件

d.NPM

下載第三方包

# 下載安裝并將依賴寫入package.json文件中
$ npm install package --save
# 安裝指定版本
$ npm install package1.0.1

發(fā)布自己的包

先要在npm注冊(cè)個(gè)賬號(hào),然后按照npm init的提示填寫相關(guān)信息,最后使用npm publish發(fā)布就好。

3.文件操作

a.fs模塊

node只提供了基本的文件操作api,但沒有文件拷貝這種高級(jí)操作,這里我們先練個(gè)手

小文件拷貝

var fs = require('fs');

function copy(src, dst) {
    fs.writeFileSync(dst, fs.readFileSync(src));
}

function main(argv) {
    copy(argv[0], argv[1]);
}

main(process.argv.slice(2));

進(jìn)入該目錄下,如果想要復(fù)制該目錄下的test.py文件到該文件夾下test2.py,鍵入命令:

$ node node-echo.js test.py test2.py

以上程序通過fs.readFileSync從源路徑讀取文件內(nèi)容,并使用fs.writeFileSync將文件寫入到目標(biāo)路徑。process是一個(gè)全局變量,通過process.argv獲得命令行參數(shù)。值得注意的是argv[0]始終為node執(zhí)行程序的絕對(duì)路徑,argv[1]為主模塊的絕對(duì)路徑,所以傳入的參數(shù)需要從argv[2]這個(gè)位置取。

大文件拷貝

上面的文件拷貝小文件沒有什么大問題,但是讀取大文件內(nèi)存會(huì)爆倉(cāng),要讀取大文件只能讀一點(diǎn)寫一點(diǎn),直至完成,對(duì)于上面的程序需要進(jìn)行如下改造。

var fs=require('fs')

function copy(src,dist){
    fs.createReadStream(src).pipe(fs.createWriteStream(dist))
}

function main(argv){
    copy(argv[0],argv[1])
}

main(process.argv.slice(2))

使用fs.createReadStream創(chuàng)建一個(gè)源文件只讀數(shù)據(jù)流,使用fs.createWriteStream創(chuàng)建一個(gè)只寫數(shù)據(jù)流,并用pipe方法將兩個(gè)數(shù)據(jù)流連接起來。

b.Buffer

js中沒有二進(jìn)制數(shù)據(jù)類型,node提供了一個(gè)與String對(duì)等的全劇構(gòu)造函數(shù)Buffer來對(duì)二進(jìn)制數(shù)據(jù)進(jìn)行操作。除了可以讀取文件得到Buffer的實(shí)例,還能夠直接構(gòu)造,如:

var bin = new Buffer([ 0x68, 0x65, 0x6c, 0x6c, 0x6f ]);

Buffer與String類型差不多,可以用length屬性讀取字節(jié)長(zhǎng)度,也可以通過[index]方式讀取文件位置。也可與String相互轉(zhuǎn)化,如:

var str=bin.toString('utf-8') // 指定編碼
var bin=new Buffer('hello','utf-8') //<Buffer 68 65 6c 6c 6f>

當(dāng)然,他們兩者也是有區(qū)別的。字符串是只讀的,他的意思不是說這個(gè)字符串不會(huì)被修改,而是單獨(dú)修改某個(gè)字節(jié)位置,這個(gè)位置并不會(huì)改變,而Buffer則不同,修改Buffer更像是修改數(shù)組,可以直接修改某個(gè)位置的值。

使用slice方法也不是返回一個(gè)新Buffer,而是返回了某個(gè)位置的指針,修改該指針的值會(huì)作用于原buffer。比如:

var bin =new Buffer([0x68,0x65,0x6c])
var bin2=bin.slice(1)
bin2[0]=0x68
console.log(bin) // 68 68 6c

所以,如果想要拷貝一份buffer,需要?jiǎng)?chuàng)建一個(gè)新buffer,通過copy方法將buffer中的數(shù)據(jù)復(fù)制過去。

var bin =new Buffer([0x68,0x65,0x6c])
var dup=new Buffer(bin.length)
bin.copy(dup)
bin[0]=0x65
console.log(bin) //65 65 6c
console.log(dup) // 68 65 6c

c.Stream

當(dāng)內(nèi)存中無法一次裝下需要處理的數(shù)據(jù),或是需要一邊讀一邊處理時(shí),我們就需要用到數(shù)據(jù)流。Node中通過各種Stream來提供對(duì)數(shù)據(jù)流的操作。比如,對(duì)數(shù)據(jù)創(chuàng)建一個(gè)只讀流:

var fs=require('fs')
var rs=fs.createReadStream(process.argv[2])

rs.on('data',function(chunk){
   // do something
    console.log(chunk) 
})

rs.on('end',function(){
    console.log('end')
})

Stream基于事件機(jī)制工作,所有Stream的實(shí)例都繼承于NodeJS提供的EventEmitter。
上面的data事件會(huì)不斷被觸發(fā),然而data事件中的函數(shù)并不會(huì)每次都執(zhí)行得過來,可以按照如下方法來解決這個(gè)問題。

var fs=require('fs')
var rs=fs.createReadStream(process.argv[2])

function print(data,func){
    console.log(data)
    func()
}

rs.on('data',function(chunk){
    rs.pause()
    print(chunk,function(){
        rs.resume()
    })
})

rs.on('end',function(){
    console.log('end')
})

創(chuàng)建一個(gè)只寫數(shù)據(jù)流

var fs=require('fs')
var rs=fs.createReadStream(process.argv[2])
var ws=fs.createWriteStream(process.argv[3])

rs.on('data',function(chunk){
    ws.write(chunk)
})

rs.on('end',function(){
    ws.end()
    console.log('end')
})

但上面的程序有個(gè)問題是,寫入速度跟不上讀取速度的話,內(nèi)部緩存會(huì)爆倉(cāng)。我們根據(jù).write方法的返回值來判斷傳入的數(shù)據(jù)是寫入目標(biāo)文件還是臨時(shí)放在了緩存中,通過drain事件,drain的意思是排干,意思是用來判斷什么時(shí)候只寫數(shù)據(jù)流已經(jīng)將緩存中的數(shù)據(jù)寫入目標(biāo),可以傳入下一個(gè)待寫數(shù)據(jù)了。

var fs=require('fs')
const argvs=process.argv
var rs=fs.createReadStream(argvs[2])
var ws=fs.createWriteStream(argvs[3])

rs.on('data',function(chunk){
    if(ws.write(chunk)===false){
        rs.pause()
    }
})

rs.on('end',function(){
    ws.end()
})

ws.on('drain',function(){ // 防爆倉(cāng)控制
    rs.resume()
})

d.其他文件操作

(a)文件屬性的讀寫

獲取文件屬性

fs.stat

var fs=require('fs')
const argvs=process.argv
fs.stat(argvs[2],function(err,stats){
    if(err){
        throw err
    }else{
        console.log(stats)
    }
})

修改讀寫權(quán)限

fs.chmod

關(guān)于設(shè)置權(quán)限707、777

var fs=require('fs')
const argvs=process.argv
function getState(path,str){
    fs.stat(path,function(err,stat){
            if(err){
                throw err
            }else{
                console.log(str+stat.mode)
            }
        })
}

getState(argvs[2],'原權(quán)限')

fs.chmod(argvs[2],0777,function(err){
    if(err){
        throw err
        console.log('讀寫失敗')
    }else{
        getState(argvs[2],'現(xiàn)權(quán)限')
    }
})

更改文件所有權(quán)

fs.chown

var fs=require('fs')
const path=process.argv[2]
function getState(path){
    fs.stat(path,function(err,stat){
            if(err){
                throw err
            }else{
                const {gid,uid}=stat
                console.log(`gid:${gid},uid:${uid}`)
            }
        })
}

getState(path)
fs.chown(path,1,0,function(err){ // 1,0分別對(duì)應(yīng)uid、gid
    if(err){
        throw err
    }else{
        console.log('changed')
        getState(path)
    }
})

(b)文件內(nèi)容讀寫

讀取文件內(nèi)容

fs.readFile

var fs=require('fs')
const path=process.argv[2]
fs.readFile(path,'utf-8',function(err,data){ // 不指定編碼的情況下,以buffer形式輸出
    if(err){
        throw err
    }else{
        console.log(data)
    }
})

讀取文件目錄

fs.readdir

fs.readdir('../',function(err,files){ //傳入目錄
    if(err){
        throw err
    }
        console.log(files)
})

寫入文件

如果文件存在,則被覆蓋

fs.writeFile

fs.writeFile('test.txt','test message~~~',function(err){
    if(err){
        throw err
    }
        console.log('saved file')
})

創(chuàng)建目錄

如果目錄存在,則拋出異常

fs.mkdir

fs.mkdir('newdir',0777,err=>{
    if(err) throw err
        console.log('created!')
})

(c)底層文件操作

打開/關(guān)閉文件

fs.open
fs.close

var fs=require('fs')
const path=process.argv[2]
fs.open(path,'w',(err,fd)=>{
    if(err) throw err
    fs.futimes(fd,1388648322,1388648322,err=>{
        if(err) throw err
        console.log('futimes done')
    fs.close(fd,()=>{
        console.log('done')
    })
    })
})

讀取文件數(shù)據(jù)

fs.read
根據(jù)指定的文件描述符fd來讀取文件數(shù)據(jù)并寫入buffer指向的緩沖區(qū)對(duì)象。相對(duì)于readFile提供了更底層的接口.

一般情況下不建議使用這種方式來讀取文件,因?yàn)樗竽闶謩?dòng)管理緩沖區(qū)和文件指針,尤其是在 你不知道文件大小的時(shí)候,這將會(huì)是一件很麻煩的事情。

var fs = require('fs')
const path = process.argv[2]
fs.open(path, 'r', (err, fd) => {
    if (err) throw err
    let buf = new Buffer(8)
    fs.read(fd, buf, 1, 15, null, (err, bytesRead, buffer) => { // 0為偏移量  100為讀取的字結(jié)束  null為開始讀取的位置,null只從讀取位置讀取
        if (err) throw err
        console.log('bytesRead', bytesRead)
        console.log(buffer)
    })
})

根據(jù)文件描述符寫入文件

fs.write
該方法提供更底層的操作,實(shí)際應(yīng)用中建議使用多 fs.writeFile()

var fs = require('fs')
const path = process.argv[2]
fs.open(path, 'w', (err, fd) => {
    if (err) throw err
    const data='# hello python!'
    const buf=new Buffer(data,'utf-8')
    fs.write(fd,buf,0,data.length,0,(err,bytesWritten,buffer)=>{
        if(err) throw err
        console.log(bytesWritten)
        console.log(buffer)

        fs.close(fd,err=>{
            if(err) throw err
                console.log('file closed')
        })
    })
}

以上介紹的方法都是以異步的方式調(diào)用的,也分別都有對(duì)應(yīng)的同步方法,拿readFileSync舉例:

var fs = require('fs')
const path = process.argv[2]
try{
    const data=fs.readFileSync(path)
    console.log(data)
}catch(err){
    console.log(err)
}

e.Path

node中提供了幾個(gè)內(nèi)置模塊來簡(jiǎn)化路徑相關(guān)操作,并提升代碼可讀性。

路徑標(biāo)準(zhǔn)化

path.normalize(path)
將傳入的路徑轉(zhuǎn)換為標(biāo)準(zhǔn)的路徑,可以去掉多余的斜杠。但在不同操作系統(tǒng)下,解析后的斜杠不一樣。

var path=require('path')
const url = process.argv[0]
console.log(path.normalize(url))

連接路徑分隔符

將path片段連接在一起并將路徑規(guī)范化。
path.join

var path=require('path')
console.log(path.join('/foo','//bar','abc','../abc')) // \foo\bar\abc

獲取path的擴(kuò)展名

返回文件的擴(kuò)展名,從最后一個(gè).截取,沒有.則返回空字符串。
path.extname

const url = process.argv[2]
var path=require('path')
console.log(path.extname(url)) // .py
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,763評(píng)論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,238評(píng)論 3 428
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,823評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,604評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,339評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,713評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,712評(píng)論 3 445
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,893評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,448評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,201評(píng)論 3 357
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,397評(píng)論 1 372
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,944評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,631評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,033評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,321評(píng)論 1 293
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,128評(píng)論 3 398
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,347評(píng)論 2 377

推薦閱讀更多精彩內(nèi)容

  • https://nodejs.org/api/documentation.html 工具模塊 Assert 測(cè)試 ...
    KeKeMars閱讀 6,361評(píng)論 0 6
  • Node.js是目前非常火熱的技術(shù),但是它的誕生經(jīng)歷卻很奇特。 眾所周知,在Netscape設(shè)計(jì)出JavaScri...
    w_zhuan閱讀 3,630評(píng)論 2 41
  • 個(gè)人入門學(xué)習(xí)用筆記、不過多作為參考依據(jù)。如有錯(cuò)誤歡迎斧正 目錄 簡(jiǎn)書好像不支持錨點(diǎn)、復(fù)制搜索(反正也是寫給我自己看...
    kirito_song閱讀 2,487評(píng)論 1 37
  • Node.js是目前非常火熱的技術(shù),但是它的誕生經(jīng)歷卻很奇特。 眾所周知,在Netscape設(shè)計(jì)出JavaScri...
    Myselfyan閱讀 4,084評(píng)論 2 58
  • 看別人寫的怎么學(xué)英語(yǔ)的文章,什么聽說讀寫,聽播客,看外國(guó)大片,和老外聊天,我想說能不能結(jié)合點(diǎn)實(shí)際的,大家都要工作沒...
    羅蓁蓁閱讀 385評(píng)論 2 4