NodeJS學習記錄

記錄一下nodejs的學習之路javascript

NodeJS相關概念

阻塞和非阻塞

阻塞I/O:I/O時進程休眠等待I/O完成後進行下一步
非阻塞I/O:I/O時函數當即返回,進程不等待I/O完成前端

事件驅動

I/O等異步操做結束後的通知
觀察者模式java

NodeJS是什麼?

  • Node.js is a JavaScript runtime built on Chrome's V8
  • Node.js uses an event-driven,non-blocking I/O model

爲何偏心NodeJS?

CPU密集:壓縮、解壓、加密、解密node

I/O密集:文件操做、網絡操做、數據庫操做git

  • 前端職責範圍變大,統一開發體驗
  • 在處理高併發,I/O密集場景(web場景)性能優點明顯

web常見場景程序員

  • 靜態資源的獲取
  • 數據庫操做
  • 渲染頁面

高併發應對之道

  • 增長機器數、負載均衡
  • 增長每臺機器的CPU數-多核

進程:是計算機中的程序關於某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位。
線程:進程內一個相對獨立的、可調度的執行單元,與同屬一個進程的線程共享進程的資源
多進程:啓動多個進程,多個進程能夠一塊執行多個任務web

NodeJS工做模型

NodeJS的單線程

單線程只是針對主進程,I/O操做系統底層多線程調度
Node單線程並非單進程(node有一個集羣(cluster)模塊用來處理多進程,cpu有幾個核就啓動幾個進程)chrome

經常使用場景

  • Web Server
  • 本地代碼構建(如今前端各類框架、ES六、模塊化等的出現,前端代碼變得異常的複雜,沒法直接在瀏覽器上運行,須要一些轉化工做):雖然是I/O操做比較少,也許就幾個文件,主要是CPU運算,可是這些都是在處理前端代碼,有不少前端的邏輯
  • 實用工具開發:爬蟲(雖然並非最佳)

環境

CommonJS數據庫

  • 每一個文件是一個模塊,有本身的做用域
  • 在模塊內部module變量表明模塊自己
  • module.exports屬性表明模塊對外接口

global(Node沒有BOM,DOM)
process 當前執行的進程, 掛載在global下面npm

require規則
/表示絕對路徑,./表示相對於當前文件的路徑
支持js,json,node拓展名,不寫依次嘗試
不寫路徑則認爲是build-in模塊或者各級node_modules內的第三方模塊。
require特性

module被加載的時候執行,加載後緩存

// a.js
console.log('This is a module!')
const testVar = 100
function test () {
    console.log(testVar)
}

module.exports.testVar = testVar
module.exports.testFn = test

// b.js
var mod = require("./a.js")
console.log(mod.testVar)
mod.testFn()
// This is a module!
// 100
// 100

// c.js
require("./a.js")
require("./a.js")
// This is a module!

一旦出現某個模塊被循環加載,就只輸出已經執行的部分,還未執行的部分不會輸出(要避免循環加載)

// modA.js
module,exports.test = 'A'

const modB = require('./modB')
console.log('modA: ', modB.test)

module.exports.test = 'AA'

// modB.js
module,exports.test = 'B'

const modA = require('./modA')
console.log('modB: ', modA.test)

module.exports.test = 'BB'

// main.js
var modA = require('./modA')
var modB = require('./modB')
console.log(modA.test)
console.log(modB.test)

// modB : A
// modA: BB
// AA
// BB

module.exports和exports的區別

exports = module.exports
在通常狀況下,exports就是module.exports的快捷方式;
能夠向exports裏添加屬性,但不能改變exports的指向(改變了指向後和普通的對象沒什麼區別)。

(
    function(exports, require, module, _filename, _dirname){
          // code
     }
);
好比:
exports.test = 100; // 能夠正常引用
exports={               // 改變了exports的指向,require後,mod.test是undefined。
    test: 100,
    a: 200
}
module.exports={               // 正常,mod.test是100
    test: 100,
    a: 200
}

基本API

process

// a.js
const {argv, argv0, execArgv, execPath} = process;

argv.forEach(item => {  // 查看參數,比較經常使用
    console.log(item)
});
console.log(argv0)
console.log(execArgv)
console.log(execPath)  // 執行腳本的路徑

命令行輸入:node --inspect a.js --test a=1 b=2
/* /usr/local/bin/node
   /Users/nodejs/demos/a.js
   --test
   a=1
   b=2
   node
   [ '--inspect' ]
      /usr/local/bin/node
*/

// process 執行環境相關
const {env} = process;
console.log(env);

console.log(process.cwd()); // 打印當前命令執行的路徑,和Linux的pwd同樣

timer

setImmediate(() => {             // 下一個隊列的隊首
    console.log('setImmediate');
});

setTimeout(() => {               // 二者之間
    console.log('setTimeout')
}, 0);

process.nextTick(() => {         // 當前隊列的最後一個,會影響後面正常異步的執行
    console.log('nextTick');
        process.nextTick(() => {
            console.log('nextTick');
        })
})

/* nextTick             idle
    nextTick             
   setTimeout          io
   setImmediate      check
*/

調試
node --inspect-brk a.js (加brk在入口文件停住),在谷歌瀏覽器裏打開chrome://inspect/#devices
直接在IDE中調試

path

const {normalize} = require('path');
// const normalize = require('path').normalize;
console.log(normalize('/usr/local/bin'));
console.log(normalize('/usr//local/../bin'));
// \usr\local\bin
// \usr\bin

// join,拼接自動處理斜線
const {join} = require('path');
console.log(join('/usr','local','bin/'));
// \usr\local\bin\

// resolve,把相對路徑轉換成絕對路徑
const {resolve} = require('path');
console.log(resolve('./'));
// C:\Users\jrxiongxiaolong\Desktop\myWork\nodeJS

const {basename, dirname, extname} = require('path');
const filePath = '/usr/loacl/bin/no.txt';
console.log(basename(filePath));
console.log(dirname(filePath));
console.log(extname(filePath));
// no.txt
// /usr/loacl/bin
// .txt

// 當要修改路徑中的某一個值的話,常常用轉化
const {parse, format} = require('path');
const ret = parse('/usr/local/bin/no.txt');
console.log(ret);
console.log(format(ret));
// { root: '/',
// dir: '/usr/local/bin',
// base: 'no.txt',
// ext: '.txt',
// name: 'no' }
// /usr/local/bin\no.txt

const {
sep,
delimiter,
win32,
posix
} = require('path');

console.log('sep: ', sep);
console.log('PATH: ', process.env.PATH);
console.log('delimiter: ', delimiter);
// sep: \
// PATH: C:\Users\jrxiongxiaolong\bin;C:\Program Files\Git\mingw64\bin;C:\Program
// Files\Git\usr\local\bin;C:\Program Files\Git\usr\bin;C:\Program Files\Git\usr\bi
// n;C:\Program Files\Git\mingw64\bin;C:\Program Files\Git\usr\bin
// delimiter: ;
console.log('win sep: ', win32.sep);
console.log('win delimiter: ', win32.delimiter);
// win sep: \
// win delimiter: ;
console.log('posix sep: ', posix.sep);
console.log('posix delimiter: ', posix.delimiter);
// posix sep: /
// posix delimiter: :

__dirname,__filename 老是返回文件的絕對路徑

process.cwd() 返回執行node命令的文件夾

./ 在require方法中老是相對當前文件所在文件夾,在其餘地方和process.pwd()同樣,相對node啓動文件夾

Buffer

掛載在global對象上

  • Buffer用於處理二進制數據流
  • 實例相似整數數組,大小固定
  • 使用C++代碼在V8堆外分配物理內存
console.log(Buffer.alloc(10));
console.log(Buffer.alloc(10, 1));
console.log(Buffer.allocUnsafe(10)); // 建立但不會初始化
console.log(Buffer.from([1,2,3]));
console.log(Buffer.from('string')); // 默認UTF-8
console.log(Buffer.from('string', 'base64'));
{/* <Buffer 00 00 00 00 00 00 00 00 00 00>
<Buffer 01 01 01 01 01 01 01 01 01 01>
<Buffer e0 0c dd b0 41 01 00 00 00 d4>
<Buffer 01 02 03>
<Buffer 73 74 72 69 6e 67>
<Buffer b2 da e2 9e> */}
/*
Buffer.byteLength()
Buffer.isBuffer()
Buffer.concat()
*/
console.log(Buffer.byteLength('test'));
console.log(Buffer.byteLength('測試')); // 所佔字節
console.log(Buffer.isBuffer([1, 2, 3])) // 判斷是否爲Buffer對象
console.log(Buffer.isBuffer(Buffer.from([1, 2, ,3])));
const buf1 = Buffer.from('This ');
const buf2 = Buffer.from('is ');
const buf3 = Buffer.from('a ');
const buf4 = Buffer.from('test');
const buf = Buffer.concat([buf1, buf2, buf3, buf4]);
console.log(buf.toString());4
// 4
// 6
// false
// true
// This is a test

/*
buf.length
buf.toString()
buf.fill()
buf.equals()
buf.indexOf()
buf.copy()
*/
const buf5 = Buffer.from('This is a test!');
console.log(buf5.length); // Buffer所佔的字節數(申請的空間)
console.log(buf5.toString('base64'));

const buf6 = Buffer.allocUnsafe(10);
console.log(buf6);
console.log(buf6.fill(3, 2, 6)); // (val, start, end)
// 15
// VGhpcyBpcyBhIHRlc3Qh
// <Buffer 09 35 1e e8 50 e9 e8 40 26 ea>
// <Buffer 09 35 03 03 03 03 e8 40 26 ea>
console.log(buf5.equals(buf6)); // false
// Buffer是一種類數組的結構
console.log(buf5.indexOf('es'));
console.log(buf5.indexOf('es1'));
// 11
// -1

// 中文編碼問題
const StringDecoder = require('string_decoder');
const decoder = new StringDecoder('utf8');
const buf7 = Buffer.from('中文字符串!');
for(let i=0; i<buf7.length; i += 5){
const b = Buffer.allocUnsafe(5);
buf7.copy(b, 0, i);
console.log(b.toString());
}
for(let i=0; i<buf7.length; i += 5){
const b = Buffer.allocUnsafe(5);
buf7.copy(b, 0, i);
console.log(decoder.write(b));
}

events (事件)

// events(事件)
// 全部能觸發事件的對象都是EventEmitter類的實例
const EventEmitter = require('events');
class CustomEvent extends EventEmitter {

}

var ce1 = new CustomEvent();
ce1.on('test', () => {
console.log('test');
})
setInterval(() => {
ce1.emit('test');
}, 1000)
// test
// test
// test
// ...

var ce2 = new CustomEvent();
ce2.once('test', () => {
console.log('test');
})
setInterval(() => {
ce2.emit('test');
}, 1000)
// test

var ce3 = new CustomEvent();
ce3.on('error', (err, date) => {
console.log(err);
console.log(date);
})
ce3.emit('error', new Error('oops!'), Date.now());

function fn1() {
console.log('fn1');
}
function fn2() {
console.log('fn2');
}
var ce4 = new CustomEvent();
ce4.on('test', fn1);
ce4.on('test', fn2);
setInterval(() => {
ce4.emit('test');
},500)
setTimeout(() => {
// ce4.removeListener('test', fn1);
ce4.removeAllListeners('test'); // 注意有's'
}, 1500)

fs(文件系統)

// 異步方法的最後一個參數都是一個回調函數,傳給回調函數的參數取決於具體方法,但回調函數的第一個參數都會保留給異常。若是操做成功,則第一個參數會是null或undefined。
const fs = require('fs');
// 讀文件,異步
fs.readFile('./a.js', 'utf8', (err,data) => {
if(err) throw err;
console.log(data); // dara.toString()
})
// 同步
const data = fs.readFileSync('./a.js', 'utf8');
console.log(data);

// 寫文件
fs.writeFile('./text', 'This is test1.', { // 能夠直接寫'utf8'
encoding: 'utf8'
}, (err) => {
if(err) throw err;
console.log('Done!')
})
const content = Buffer.from('This is test2.');
fs.writeFile('./text', content, (err) => { // 用Buffer不用寫編碼格式了
if(err) throw err;
console.log('Done!')
})

// 詳細信息
fs.stat('./a.js', (err, stats) => {
if(err) throw err;
console.log(stats.isFile()); // 是不是文件
console.log(stats.isDirectory()); // 是不是文件夾
console.log(stats);
})
// true
// false
// Stats {
// dev: 1150485948,
// mode: 33206,
// nlink: 1,
// uid: 0,
// gid: 0,
// rdev: 0,
// blksize: undefined,
// ino: 4503599627404705,
// size: 2127,
// blocks: undefined,
// atimeMs: 1532799705478.6035,
// mtimeMs: 1532630314117.9111,
// ctimeMs: 1532630314117.9111,
// birthtimeMs: 1531923690001.36,
// atime: 2018-07-28T17:41:45.479Z,
// mtime: 2018-07-26T18:38:34.118Z,
// ctime: 2018-07-26T18:38:34.118Z,
// birthtime: 2018-07-18T14:21:30.001Z }

// 重命名
fs.rename('./text', 'test.txt', err => {
if(err) throw err;
console.log('Done!');
})

// 刪除
fs.unlink('./test.txt', err => {})

// 讀文件夾,process.cwd()
fs.readdir('./', (err, files) => {
if(err) throw err;
console.log(files);
})

// 建立文件夾
fs.mkdir('./test', err => {})
// 刪除文件夾
fs.rmdir('./test', err => {})

// 監視文件或文件夾的變化,可用於本地代碼構建時一些文件的變化引發的一些配置代碼的自動改變
fs.watch('./', {
recursive: true // 是否遞歸監視
}, (eventType, filename) => { // 變化的事件類型
console.log(eventType, filename);
})

// stream,流,有方向的數據(兩個要點,方向和數據)
const fs = require('fs');
const rs = fs.createReadStream('./demo_2.js');
rs.pipe(process.stdout); // 流向控制檯,至關於fs.readFile() 但更優雅。

// const fs = require('fs');
const ws = fs.createWriteStream('./demo_2.js');  // 理論上只接受Buffer,可是String和Buffer間能夠互相轉化,Buffer.from() 和 buf.toString(),因此也接受字符串,可是不能是數字。
const tid = setInterval(() => {
var num = Math.floor(Math.random() * 10);
if(num < 7) {
console.log(num);
ws.write(num + '');
} else {
console.log(num);
clearInterval(tid);
ws.end();
}
}, 200)
ws.on('finish', () => {
console.log('done!');
})

其它

解決回調地獄問題

onst fs = require('fs');
const promisify = require('util').promisify;

const read = promisify(fs.readFile);
read('./d.js').then(data => {
console.log(data.toString());
}).catch((ex) => {
console.log(ex);
})

async function test() {
try {  // 若是await出現 reject 狀態,後面的await都不會執行。因此要用try catch處理
const content = await read('./d.js');
console.log(content.toString());
} catch (ex) {
console.log(ex);
}
}
// 若第一個await和第二個await沒有關係,那麼濫用asyc awiat是很差的,由於第二個await不必等待第一個執行完畢再去執行。之因此以同步的形式寫異步代碼,解決回調地獄,並且前一個await執行成功,才能執行下一個await。是由於回調嵌套時,只有在前一個回調成功後才能執行下一個回調,即上一個回調的執行結果和下一個回調有關係。

node項目相關

.gitignore

  • 匹配模式前 / 表明項目根目錄
  • 匹配模式最後加 / 表明是目錄 build/
  • 匹配模式前加 ! 表示取反(可能一個文件夾中的某個文件不須要被忽略)

*表明任意個字符,匹配任意一個字符,** 匹配多級目錄(正則裏的 * 是量詞)*.swp

.npmignore 規則和./gitignore同樣,有一些是不可忽略的,好比 package.json,README,CHANGELOG,LICENSE / LICENCE
EditorConfig
統一的項目的配置,不一樣的編輯器會有不同的風格,好比tab是真的tab仍是空格;最後一行是否須要一個回車;編碼方式等。
ESlint
開源的JavaScript代碼檢查工具,JavaScript是一個動態的弱類型語言,在開發中比較容易出錯。由於沒有編譯程序,爲了尋找JavaScript代碼錯誤一般須要在執行過程當中不斷的調試。像ESlint這樣的可讓程序員在編碼的過程當中發現問題而不是在執行的過程當中
ESlint的初衷是爲了讓程序員能夠建立本身的檢測規則
自定義規則,好比不容許使用 console.log() ,alert() 等
某一塊不開啓eslint的某個規則

/* eslint-disable no-console, no-alert */  
console.log(123)
/* eslint-disable */

alert('foo') // eslint-disable-line no-alert

// eslint-disable-next-line
alert('foo')
相關文章
相關標籤/搜索