process
- process.env
表示的是当前系统中的一些环境。 环境变量分为两种:用户变量、系统变量。 设置环境变量的两种语法: windows系统→ set a = 1,mac系统→ export b = 2 执行代码时传入环境
node test.js --port 8085 --method kill
let env = process.argv.slice(2).reduce((memo, current, index, arr) => {
if (current.startsWith('--')) {
memo[current.slice(2)] = arr[index+1];
}
return memo;
},{})
console.log(env);
- process.platform
查看当前的操作系统,win32 darwin 等。 - process.chdir()
change directory的缩写,用于改变当前目录的方法。 - process.cwd()
current working directory的缩写,能识别并返回当前的工作目录。 - process.argv
process.argv 打印的结果有三部分: [执行node所在的exe文件,当前执行的文件, …其他参数] console.log(process.argv) // 打印结果如下 - process.nextTick()
优先级最高的微任务,表示当前执行栈的底部。
模块化规范
common.js规范 amd cmd esm 模块, umd system.js
- 为什么要有模块化
最早是为了解决命名冲突, (‘单例模式’不能完全解决这些问题) 比如 var a = { aa(){} } ; var b = { aa() {} }; 这样就可以避免命名冲突。 前端里会有 请求的问题 (import)依赖问题(amd cmd 依赖前置 就近依赖),还需要去处理依赖问题,所以amd cmd也不靠谱。 再后来,umd 可以兼容 amd cmd common.js ,但是不支持es6. - esModule规范
每次你引用一个模块,发请求,import属于静态的,靠webpack编译后,就变成require就不会再发请求。 而不经过webpack的编译的正常的前端esModule规范,都是引入一个文件发一个请求。 vite 就是靠发请求, 对请求劫持,进行转义实现的。 es6 -> 一个文件就是一个模块,我想使用别人的就import,想给别人使用就export。 - commonjs规范
基于文件读写的,如果依赖了某个文件我就会进行文件读取,动态的。 一个文件就是一个模块,我想使用这个模块就require, 想给别人用就 module.exports导出。 因为require是动态的,可以写在判断语句里边,写在代码里,所以webpack并不能知道它有没有并用到,因此common.js规范中是不支持tree-shaking的。
eventloop
这张图主要是包括了node环境下eventloop的宏任务部分。
- 最常用的四个阶段 timers、 poll、 check、 closing Callbacks
如图所示的轮询流程,会有node api不断地去将各种类型的宏任务事件分发到各个队列。主要最常用的四个队列,就是对应图中的四个阶段timers、poll、check、close callbacks。因为pending callbacks 和Idle、prepare这两个阶段是系统内部使用,我们并不能控制 - 执行机制
一般是卡在poll阶段,不停地去检索新的IO事件进来。有定时器任务就扔进定时器队列,等哪个定时间的时间到了,就从poll跳上去执行它。同理,如果有setImmediate任务被扔进check阶段,poll也会下去执行。 整体代码从解析到开始执行,默认先执行主栈的代码,也就是先执行完同步代码。 主栈执行完毕后,就要去执行定时器,如果定时器都还没到时间,就继续向下执行 poll check 队列。 - setTimeout(()=>{},0)和 setImmediate谁先执行
就如上边说的,主栈执行完毕后会先去timer执行定时器,如果0秒的定时器在放入后被处理的足够快,该定时器的时间到了,那么就会在本次tick执行,即先setTimeout再setImmediate。 如果处理的较慢,就会先setImmediate。 用node v14.8.0测试 下边代码,当setTimeout的时间为0 1 2时都是setTimeout先执行,当3时就会是setImmediate先执行。
setTimeout(() => {
console.log('setTimeout')
},3)
setImmediate(() => {
console.log('setImmediate')
})
- setImmediate的作用
作为一个异步任务,可以保证在一轮循环中执行完毕,用的不多,但是在node eventloop中有着绝对地位,有专属的check阶段。 - poll阶段
当poll阶段里一轮的IO任务执行不完,就会推迟到下一轮的Idle,prepare阶段里边,Idle、prepare阶段是专门执行延迟到下一个勋魂迭代的IO回调。 一般情况下,poll阶段里有多个IO任务,且会将它们都执行完了,再去别的阶段check或者timer,在poll亦或者是timer check这些队列里,有多个宏任务,都是保持执行一个宏任务清空一次微任务的机制。 - 浏览器eventloop和node eventloop对比
在node版本11之后,两者的eventloop非常相似,都是先执行主栈代码,完成后会先清空一次微任务,然后开始执行第一个宏任务,每执行完一个宏任务都会清空一次微任务。 因为新版的node要和浏览器表现形式一致,所以这样设计的。
补充
关于node中的this
前端中访问变量是通过window,后端中访问变量global。 node中的this不等于global。 用node执行文件的时候,它会把这个文件当成一个模块,默认把this修改,全局作用域的的this是空对象。全局定义的箭头函数的this也是全局的this,所以也是空对象。在node中很少提this。IIFE和function定义的函数里的this都是global。
buffer相关
Buffer是node中的二进制对象,最早的时候 浏览器不能直接读文件,node作为服务端存文件的时候,总不能把文件存成字符串,只能存成二进制。Buffer就解决了文件读写的。
编写命令行工具
const program = require('commander');
program.option('-p,--port <n>', 'set user port');
program.parse(process.env);
const options = program.opts();
if (options.port) {
}
五个不在global上的全局属性
global上有属性可以直接访问叫全局属性。 但是require exports module __dirname __filename 也可以直接访问,但他们不在global上。 node中,每个文件都一个模块,模块化的实现借助的是 函数。 node内部做的也是这样一件事,相当于给每个文件包裹了一个函数 即IIFE。 并给IIFE传入了五个参数即require exports module __dirname __filename,因此每个文件(模块)都可以直接使用这个五个不在global的全局属性。
多线程和单线程的优缺点
- 多线程
优点: 可以同时 处理多个请求,适合cpu密集型 缺点: 如果多个线程操作同一个资源 得上锁 群发短信 多线程并不是一起去干一些事,而是靠的是切换上下文(浪费一些性能) - 单线程
优点: 不需要开启多个线程 节省资源 ,不适合做大量CPU操作。 开启子进程,可以把复杂的操作放到子进程。 开启多个子进程可以解决多线程才能调用多核CPU的问题。
- Nginx 和 Go 在处理高并发的时候 也是类似与Node这种单个线程来调度,异步执行任务。
|