記錄一下nodejs的學習之路javascript
阻塞I/O:I/O時進程休眠等待I/O完成後進行下一步
非阻塞I/O:I/O時函數當即返回,進程不等待I/O完成前端
I/O等異步操做結束後的通知
觀察者模式java
- Node.js is a JavaScript runtime built on Chrome's V8
- Node.js uses an event-driven,non-blocking I/O model
CPU密集:壓縮、解壓、加密、解密node
I/O密集:文件操做、網絡操做、數據庫操做git
web場景
)性能優點明顯web常見場景程序員
進程:是計算機中的程序關於某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位。
線程:進程內一個相對獨立的、可調度的執行單元,與同屬一個進程的線程共享進程的資源
多進程:啓動多個進程,多個進程能夠一塊執行多個任務web
單線程只是針對主進程,I/O操做系統底層多線程調度
Node單線程並非單進程(node有一個集羣(cluster)模塊用來處理多進程,cpu有幾個核就啓動幾個進程)chrome
CommonJS數據庫
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 }
// 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同樣
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中調試
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啓動文件夾
掛載在global對象上
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(事件) // 全部能觸發事件的對象都是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)
// 異步方法的最後一個參數都是一個回調函數,傳給回調函數的參數取決於具體方法,但回調函數的第一個參數都會保留給異常。若是操做成功,則第一個參數會是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。是由於回調嵌套時,只有在前一個回調成功後才能執行下一個回調,即上一個回調的執行結果和下一個回調有關係。
.gitignore
*
表明任意個字符,?
匹配任意一個字符,**
匹配多級目錄(正則裏的 * 是量詞)*.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')