记录一下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')