process
是一个全局的变量,因此使用的时候是不须要执行require
操做,能够直接使用。css
这里分两部分来讲明,第一个就是能够借助它去获取进程信息,好比进程工做的时候本地是一个什么样的环境,经过process
能够获取。第二个经过process
能够对当前的进程作一些操做,好比说能够监听进程执行过程当中内置的事件,建立子进程完成更多的操做。html
// 查看内存消耗
console.log(process.memoryUsage());
/** * rss: 常驻内存 * heapToal: 总内存大小 * heapUsed: 已使用内存 * external: 扩展内存 - 底层模块占用的C/C++核心模块 * arrayBuffers: 缓冲区大小 */
复制代码
console.log(process.cpuUsage());
/** * user: 用户占用的时间片断 * system: 系统占用的时间片断 */
复制代码
process.cwd(); // 运行目录
process.version; // Node版本
process.versions; // 运行环境版本
process.arch; // cpu架构
process.env.Node_ENV; // 环境 须要先设置
process.env.PATH; // 环境变量
process.env.USERPROFILE; // 管理员目录路径 不一样环境方式不同 process.env.HOME
process.platform; // 平台 win32 macos
复制代码
process.argv; // 获取运行参数,空格分隔可在数组中获取到,默认会存在Node目录和执行脚本的目录两个值。
process.argv0; // 获取第一个值, 只有这一个api
process.pid; // 获取运行的pid
process.ppid;
process.uptime; // 脚本运行时间
复制代码
事件监听在process
中提供的内容。这里不会着重说明process
里面到底有哪些事件,主要仍是看一看在NodeJS
里面熟悉一下事件驱动编程以及发布订阅的模式。前端
process
是实现了emit
接口的。可使用on
监听事件,内部提供了不少的事件,好比exit
,程序退出的时候执行。这里绑定的事件只能执行同步代码,是不能够执行异步代码的,这里要注意。linux
process.on('exit', (code) => { // 退出时
console.log(code); // 0
})
process.on('beforeExit', (code) => { // 退出以前
console.log(code); // 0
})
复制代码
手动退出,这种退出不会执行beforeExit
,并且exit
后面的代码也不会执行,由于执行exit
就已经退出了。算法
process.exit();
复制代码
process.stdout; // 是一个流,能够对他进行读写操做。
process.stdout.write('123'); // 123
复制代码
const fs = require('fs');
fs.createReadStream('text.txt').pipi(process.stdout); // 读取文件输出。
复制代码
process.stdin; // 能够拿到控制台输入的内容
process.stdin.pipe(process.stdout); // 输入以后输出
复制代码
// 设置字符编码
process.stdin.setEncoding('utf-8');
// 监听readable事件,是否可读也就是有无内容
process.stdin.on('readable', () => {
// 获取输入的内容
let chunk = process.stdin.read();
if (chunk !== null) {
process.stdout.write(chunk);
}
})
复制代码
Node
中的内置模块,能够直接使用require
将它导入,他的主要做用就是处理文件的目录和路径。只须要调用不一样的方法。path
至关于一个工具箱,只须要掌握它里面提供的工具,也就是方法。macos
const path = require('path');
复制代码
获取路径中基础名称编程
path.basename(__filename); // test.js
// 传入第二个参数若是匹配会省略后缀,不匹配仍旧返回真实的后缀
path.basename(__filename, '.js'); // test
path.basename('/a/b/c'); // c
path.basename('/a/b/c/'); // c
复制代码
获取路径中的目录名称api
path.dirname(__filename); // d:\Desktop\test
path.dirname('/a/b/c'); // /a/b
path.dirname('/a/b/c/'); // /a/b
复制代码
获取路径中的扩展名称数组
path.extname(__filename); // .js
path.extname('/a/b'); //
path.extname('/a/b/index.html.js.css'); // .css
path.extname('/a/b/index.html.js.'); // .
复制代码
获取路径是不是绝对路径浏览器
path.isAbsolute('a'); // false
path.isAbsolute('/a'); // true
复制代码
拼接多个路径片断,还原成完整可用路径
path.join('a/b', 'c', 'index.html'); // a/b/c/index.html
path.join('/a/b', 'c', 'index.html'); // /a/b/c/index.html
path.join('a/b', 'c', '../', 'index.html'); // a/b/index.html
复制代码
返回一个绝对路径
path.resove(); // 获取绝对路径
复制代码
解析路径
const obj = path.parse('/a/b/c/index.html');
/** * root: / * dir: /a/b/c * base: index.html * ext: .html * name: index */
复制代码
序列化路径,与parse
功能相反, 将对象拼接成完整的路径。
path.format({
root: '/',
dir: '/a/b/c',
base: 'index.html',
ext: '.html',
name: 'index'
});
// /a/b/c/index.html
复制代码
规范化路径,将不可用路径变为可用路径, 这个方法注意若是有转译字符会转译。
path.normalize('a/b/c/d'); // a/b/c/d
path.normalize('a//b/c../d'); // a/b/c../d
path.normalize('a\\/b/c\\/d'); // a/b/c/d
复制代码
Buffer
通常称为缓冲区,能够认为由于Buffer
的存在让开发者可使用js
操做二进制
。IO
行为操做的就是二进制数据。NodeJS
中的Buffer
是一片内存空间。他的大小是不占据V8
内存大小的,Buffer
的内存申请不是由Node
生成的。只是回收的时候是V8
的GC
进行回收的。
Buffer
是NodeJS
中的一个全局变量,无需require
就能够直接使用。通常配合stream
流使用,充当数据的缓冲区。
alloc
能够建立指定字节大小的Buffer
,默认没有数据
allocUnsafe
建立指定大小的Buffer
可是不安全,使用碎片的空间建立Buffer
,可能存在垃圾脏数据,不必定是空的。
from
接收数据建立Buffer
在v6
版本以前是能够经过实例化建立Buffer
对象的,可是这样建立的权限太大了,为了控制权限,就限制了实例化建立的方式。
// 建立Buffer
const b1 = Buffer.alloc(10);
const b2 = Buffer.allocUnsafe(10);
复制代码
from
建立Buffer
能够接收三种类型,字符串,数组,Buffer
。 第二个参数是编码类型。
const b3 = Buffer.from('1');
const b4 = Buffer.from([1, 2, 3]);
复制代码
Buffer
的一些常见实例方法。
fill: 使用数据填充Buffer
,会重复写入到最后一位
write:向Buffer
中写入数据,有多少写多少,不会重复写入。
toString: 从Buffer
中提取数据
slice: 截取Buffer
indexOf:在Buffer
中查找数据
copy: 拷贝Buffer
中的数据
Buffer
的静态方法。
concat: 将多个Buffer
拼接成一个新的Buffer
isBuffer: 判断当前数据是不是一个Buffer
Buffer
的split
方法实现。
Array.Buffer.splice = function(sep) {
let len = Buffer.form(sep).length;
let ret = [];
let start = 0;
let offset = 0;
while(offset = this.indexOf(sep, start) !== -1) {
ret.push(this.slice(start, offset))
start = offset + len;
}
ret .push(this.slice(start));
return ret;
}
复制代码
在Node
中Buffer
和Stream
随处可见,他们用于操做二进制数据。
fs
是一个内置的核心模块,全部与文件相关的操做都是经过fs
来进行实现的,好比文件以及目录的建立,删除,信息的查询或者文件的读取和写入。
若是想要操做文件系统中的二进制数据须要使用fs
模块提供的API
,这个过程当中Buffer
和Stream
又是密不可分的。
介绍fs
模块以前咱们首先须要介绍一下文件系统的基础知识,好比权限位,标识符,文件描述符等。
权限是指当前的操做系统内不一样的用户角色对于当前的文件能够执行的不一样权限操做,文件的权限操做被分为r
,w
,x
三种, r
是读权限,w
是写权限,x
是执行权限。若是用8进制的数字进行表示r
是4
,w
是2
,x
是1
,若是不具有该权限就是一个0
。
操做系统中将用户分为三类分别是文件的全部者,通常指的是当前用户本身,再有就是文件的所属组,相似当前用户的家人,最后是其余用户也就是访客用户。
Node
中flag
表示对文件操做方式,好比是否可读可写。
r: 表示可读
w: 表示可写
s: 表示同步
+: 表示执行相反操做
x: 表示排他操做
a: 表示追加操做
fd
就是操做系统分配给被打开文件的标识,经过这个标识符文件操做就能够识别和被追踪到特定的文件。不一样操做系统之间是有差别的,Node
为咱们抹平了这种差别。
Node
每操做一个文件,文件描述符就会递增一次,而且它是从3
开始的。由于0
,1
,3
已经被输入,输出和错误占用了。后面咱们在使用fs.open
打开文件的时候就会获得这个fd
。
fs
任何文件操做api
都有同步和异步两种方式,这里只演示异步API
,同步基本也相同
readFile: 从指定文件中读取数据
const fs = require('fs');
const path = require('path');
fs.readFile(path.resolve('aaa.txt'), 'utf-8', (err, data) => {
console.log(err);
console.log(data);
})
复制代码
writeFile: 向指定文件中写入数据
fs.writeFile('bbb.txt', 'hello', {
mode: 438, // 操做位
flag: 'w+',
encoding: 'utf-8'
}, (err) => {
console.log(err);
})
复制代码
appendFile: 追加的方式向指定文件中写入数据
fs.appendFile('bbb.txt', 'hello', {}, (err) => {
console.log(err);
})
复制代码
copyFile: 将每一个文件中的数据拷贝到另外一个文件
fs.copyFile('aaa.txt', 'bbb.txt', (err) => {
console.log(err);
})
复制代码
watchFile: 对指定文件进行监控
fs.watchFile('bbb.txt', {
interval: 20 // 20ms监控一次
}, (curr, prev) => {
console.log(curr); // 当前信息
console.log(prev); // 前一次信息
if (curr.mtime !== prev.mtime) {
// 文件被修改了
}
})
fs.unwatchFile('bbb.txt'); // 取消监控
复制代码
前面咱们使用了fs
实现了文件的读写操做,既然已经读写了就证实已经实现了文件的打开,为何Node
还要单独的提供打开关闭的api
呢?
由于readFile
和writeFile
的工做机制是将文件里的内容一次性的所有读取或者写入到内存里,而这种方式对于大致积的文件来说显然是不合理的,所以须要一种能够实现边读编写或者边写边读的操做方式,这时就须要文件的打开、读取、写入、关闭看作是各自独立的环节,因此也就有了open
和close
。
const fs = require('fs');
const path = require('path');
// open
fs.open(path.resolve('aaa.txt'), 'r', (err, fd) => {
console.log(err);
console.log(fd);
fs.close(fd, (err) => {
});
})
复制代码
access: 判断文件或目录是否具备操做权限
fs.access('aaa.txt', (err) => {
console.log(err); // 存在错误就是没有权限
})
复制代码
stat: 获取目录及文件信息
fs.stat('aaa.txt', (err, stat) => {
console.log(stat); // size isFile(), isDirectory()
})
复制代码
mkdir: 建立目录
fs.mkdir('a/b/c', {
recursive: true, // 递归建立
}, (err) => {
console.log(err);
})
复制代码
rmdir: 删除目录
fs.rmdir('a', {
recursive: true, // 递归删除
}, (err) => {
console.log(err);
})
复制代码
readdir: 读取目录中内容, 不会递归子目录
fs.readdir('a', (err, files) => {
console.log(files);
})
复制代码
unlink: 删除指定文件
fs.unlink('a', (err) => {
console.log(err);
})
复制代码
CommonJS
的出现是为了解决前端模块化,他的做者但愿能够倒逼浏览器们实现前端模块化,可是因为浏览器自己具有的单线程阻塞的特色,CommonJS
并不能适用于浏览器平台。CommonJS
是一个超集,他是语言层面的规范,模块化只是这个规范中的一个部分。
Node
中经过EventEmitter
类实现事件统一管理。实际开发中基本不多引入这个类。这个类大部分供内置模块使用的, 好比fs
、http
都内置了这个模块。
Node
是基于事件驱动的异步操做架构,内置events
模块,模块提供了EventEmitter
类,他的实例对象具有注册事件和发布事件删除事件的常规操做。
on:添加当事件被触发时调用的回调函数
emit: 触发事件,按照注册的顺序调用每一个事件监听器
once: 注册执行一次的监听器
off:移除特定的监听器
const EventEmitter = require('events');
const ev = new EventEmitter();
ev.on('event', () => {
})
ev.emit('event');
复制代码
流
并非NodeJS
首创的内容,在linux
系统中可使用ls | grep *.js
命令操做,其实就是将ls
命令获取到的内容交给grep
去处理,这就是一个流操做。
使用流能够从空间和时间上提高效率,NodeJS
诞生之初就是为了提升IO
性能,其中最经常使用的文件系统和网络他们就是流操做的应用者。
NodeJS
中流就是处理流式数据的抽象接口,NodeJS
中的stream
对象提供了用于操做流的对象。对于流来讲只有多使用才能加深了解。
流的分段处理能够同时操做多个数据chunk
,同一时间流无须占据大内存空间。流配合管道,扩展程序会变得很简单。
NodeJS
中内置了stream
模块,它实现了流操做对象。Stream
模块实现了四个具体的抽象。全部的流都继承自EventEmitter
。
Readable: 可读流,可以实现数据的获取。
Writeable: 可写流,可以实现数据的写操做。
Duplex: 双工流,便可度又可写。
Tranform: 转换流,可读可写,还能实现数据转换。
const fs = require('fs');
const rs = fs.createReadStream('./a.txt');
const ws = fs.createWriteStream('./b.txt');
rs.pipe(ws);
复制代码
生产供消费的数据流。
const rs = fs.createReadStream('./a.txt');
复制代码
const { Readable } = require('stream');
const source = ['a', 'b', 'c'];
class MyReadable extends Readable {
constructor() {
super();
this.source = source;
}
_read() {
const data = this.source.shift() || null;
this.push(data);
}
}
const myreadable = new MyReadable(source);
myreadable.on('data', (chunk) => {
console.log(chunk.toString());
})
复制代码
用于消费数据的流,响应。
const ws = fs.createWriteStream('./b.txt');
复制代码
const { Writable } = require('stream');
class MyWriteable extends Writable {
constructor() {
super();
}
_write (chunk, en, done) {
process.stdout.write(chunk.toString());
process.nextTick(done);
}
}
const mywriteable = new MyWriteable();
mywriteable.write('yindong', 'utf-8', () => {
consoel.log('end');
})
复制代码
const { Duplex } = require('stream');
class MyDuplex extends Duplex {
constructor(source) {
super();
this.source = source;
}
_read() {
const data = this.source.shift() || null;
this.push(data);
}
_write(chunk, en, next) {
process.stdout.write(chunk);
process.nextTick(next);
}
}
const source = ['a', 'b', 'c'];
const myDuplex = new MyDuplex(source);
mtDuplex.on('data', (chunk) => {
console.log(chunk.toString());
})
mtDuplex.write('yindong', () => {
console.log('end');
})
复制代码
const { Transform } = require('stream');
class MyTransform extends Transform {
constructor() {
super();
}
_transform(chunk, en, cb) {
this.push(chunk.toString().toUpperCase());
cb(null);
}
}
const t = new MyTransform();
t.write('a');
t.on('data', (chunk) => {
console.log(chunk.toString());
})
复制代码
链表是一种数据存储结构。
在文件可写流的write
方法工做的时候,有些被写入的内容须要在缓冲区排队等待的,并且遵循的是先进先出的规则,为了保存这些排队的数据,在新版的Node
中就采用了链表的结构来存储这些数据。
相比较数组来讲,链表的优点更明显,在多个语言下,数组存放数据的长度是有上限的,数组在执行插入或者删除操做的时候会移动其余元素的位置,而且在JS
中数组被实现成了一个对象。因此在使用效率上会低一些。
固然这都是相对的,实际应用中数组的做用仍是很强大的。
链表是由一系列节点组成的集合。这里的节点都称为Node
节点,每一个节点的身上都有一个对象的引用是指向下一个节点,将这些指向下一个节点的引用组合到一块儿也就造成了一个链,对于链表结构来讲咱们常听到的会有不一样类型,双向链表,单向链表,循环链表。经常使用的通常是双向链表。
断言, 若是不符合,会中止程序,并打印错误
const assert = require('assert');
// assert(条件, 一段话)
function sum(a, b) {
assert(arguments.length === 2, '必须有两个参数');
assert(typeof a === 'number', ‘第一个参数必须是数字’);
}
复制代码
使用c语言写的插件,在Node中使用。
多线程
一个程序就是一个进程,一个进程会有多个线程 进程和进程是严格隔离的,拥有独立的执行空间。 同一个进程内的全部线程共享一套空间、代码
成本高,速度慢,安全,进程间通讯麻烦,写代码简单。
成本低,速度快,不安全,线程间通讯简单,写代码复杂。
Child_Processes
, Cluster
, Process
。
获取命令行参数
签名,完成加密算法的
const cryptp = require("crypto");
let obj = cryto.createHash('md5');
obj.update('123');
console.log(obj.digest('hex')); // MD5 32位
复制代码
系统相关
const os = require('os');
// 获取cpu个数
os.cpus();
复制代码
事件队列
const Event = require('events').EventEmitter;
let ev = new Event();
// 绑定事件
ev.on('msg', function(a, b,c) {
console.log(a, b, c); // 12 , 3, 8
})
// 派发事件
ev.emit('msg', 12,3,8);
复制代码
本身实现一个Events
:
class EventEmitter {
constructor() {
this._events = {};
}
on(eventName, callBack) {
if (this._events[eventName]) {
this._events[eventName].push(callBack);
} else {
this._events[eventName] = [callBack];
}
}
emit(eventName) {
this._events[eventName].forEach(fn => {
fn();
})
},
off(eventName, callBack) {
this._events[eventName] = this._events[eventName].filter(fn => fn !== callBack)
}
}
复制代码
请求url
模块
const url = require('url');
url.parse('url', true); // 会解析出url和参数,包含query-string的功能。
复制代码
TCP
稳定 Net
UDP
快 UDP/Datagram
DNS/Domain
域名解析成ip
const dns = require('dns');
dns.resolve('baidu.com', (err, res) => {
})
复制代码
基于net
的http
服务
const http = require('http');
const server = http.createServer((request, response) => {
// request 为请求数据
// response 为响应数据
// 设置返回值类型
res.writeHead(200, {'Content-type' : 'text/html'});
response.write = 'server start';
return response.end(); // 结束
});
server.listen(3000); // 服务监听的端口号
复制代码