全局对象

在前面关于node模块的学习过程中,我们已经知道在编译执行的过程中,通过闭包的方式,来将一个模块进行包裹,且追加自定义变量属性requireexportsmodule__dirname__filename,因此,每一个 在node环境下的模块都拥有这几个属性,因此也就成为了"模块中公共访问的变量:全局变量"(其实还是局部变量的感觉,只在各个模块中有效,然后各个模块在node模块中统一访问,指向同一个方法)。 👇 来一一解读关于各个变量都有什么作用?如何使用的!!

模块件的全局变量

require

通过调用该方法,实现从node模块中导入对应的对象/方法,可以从自己编写的模块中导入模块,也可以从node库中导入。 :point_down: 这里附带上require的一个简写逻辑,帮助更好的理解关于module.exportsexports两者的关系

require的简写逻辑

:point_up: 这里我们看出其实module.exportsexports都指向的是一个中间临时函数对象,在加载的时候,通通采用的这种逻辑来实现的! 这也就解释了为什么 :u6709: :point_down: 的一个输出情况了:

    function xxx(){}
    const yy = 123;
    const zz = 'abc';

    module.exports  = {
        xxx,
        yy
    }
    exports.zz = zz;

    console.info('来自于第一个文件的引用');
    console.info(module.exports);
    console.info(exports);

module.exports与exports

exports

指向module.exports所指向的对象,而不是网上一些说法:exports指向module.exports对象,因为根本就不是同一个,而是指向的同一个对象,当在模块中定义了module.exports的时候,两者的区别就比较明显了, 一般通过在exports对象上添加对象/方法来实现将模块中的部分对象/方法暴露出来,供给require()使用。 :dizzy_face: 关于modulemodule.exportsexports三者的关系,傻傻分不清? module.exports的过程 通过 :point_up: 这里可以将三者的关系有了一个清晰的认知!

module

代表当前js在node环境中的模块引用表示,使用module.exports可以用来定义模块导出的内容,并供给require()使用!

__dirname

当前js模块所在的目录,与path.dirname()方法返回值一样

__filename

当前js模块的绝对路径,跟随者所在的系统而定。

程序运行时的上下文--process

作为当前node程序app的控制器,提供应用程序的上下文管理,这有点类似于Android中的Application,可以将一些全局的变量/方法/对象直接存储到全局对象process中,任何node模块都可以访问到 导入方式:

  const process = require('node:process');

process对象也是EventEmitter事件对象的一个实例,也就是说该对象可以使用事件的相关api来进行异步I/O调用以及监听操作!!! 源码所在位置:lib/process.js 相关API一览: process模块

监听事件

  1. 程序退出(beforeExit与exit): 当nodejs程序的事件循环队列为 :u7a7a: 时,没有待消费的事件被丢出来,程序将会回调该方法,而且,我们可以在该方法中进行一个同步调用,延长程序的生命周期
     const process = require('node:process');
     process.on('beforeExit', code => {
         console.info('程序即将要退出了,code=' + code);
         // 这里禁止在beforeExit一直加上这个动作,因为它将会让事件循环队列一直非空,导致程序一直处于消费最后一个事件的死循环中!!!
         // setTimeout(() => {
         //     console.info('程序即将退出,额外再调用多一个动作');
         // }, 500);
     });
     process.on('exit', code => {
         console.debug('程序退出,code=', code);
         setTimeout(() => {
             console.debug('程序退出了,我将不会被执行到!!');
         }, 0);
     });
     console.info('程序启动了');
    
    程序退出的自动回调

:confused: 那么关于beforeExit的使用,应该减少在回调中发起另外的一些异步调用动作,避免事件循环队列中一直非空,而且每消费最后一个事件后,又又又再怼入了一个一个事件,因此我们应当是在这个回调中执行一些同步性的操作! 而如果在exit中执行异步调用的话,将会直接是放弃掉这个调用!

关于程序的退出code的情况,具体见底部 :point_down: 的一个描述

  1. 异步异常(unhandledRejection与rejectionHandled) :one: 当一个promise被reject并且在下一次事件循环之前被处理,则会发起rejectionHandled事件,代表reject异常得到处理;

:two: 当一个promise被reject的时候,则会触发unhandledRejection事件;

    const process = require('node:process');
    const unhandledRejections = new Map();
    process.on('unhandledRejection', (reason, promise) => {
        console.info('unhandledRejection接收到了', reason, promise);
        unhandledRejections.set(promise, reason);
    });
    process.on('rejectionHandled', promise => {
        console.info('rejectionHandled接收到了', promise);
        unhandledRejections.delete(promise);
    });

    new Promise((resolve, reject) => {
        reject(123);
    });

promise未被处理 :point_up: 这里一个promise未被处理,因此unhandledRejection事件会被触发,而如果我们将promise的发起与处理,进行一个 处理的话,那么rejectionHandled事件将会被触发!

new Promise((resolve, reject) => {
    reject(123);
}).then(null, err => {
    setTimeout(() => {
    console.info(err, '异常被处理了');
}, 3000);
});

promise被处理

:stars: 从上述基本上我们可以知晓关于这两个事件的一个使用场景:当一个promise的reject被reject的时候,如果没有进行及时的catch, 那么,我们则可以使用这个unhandledRejection来捕获,并记录当前异步调用的对象信息,而当reject的promise被及时处理的时候,我们标记为已 删除的promise,这可以用来监控整个程序在对于为捕获的异常是如何处理的,以此来提高自身编码能力!!!

  1. 异常监控(uncaughtException与uncaughtExceptionMonitor) :point_right: uncaughtException事件有点类似于android中的application的全局异常捕获机制,当一个未知的异常发生的时候,将会触发这个事件, 而且让当前的程序的退出编码为1,而非正常退出, ```javascript const process = require('node:process');

process.on('uncaughtException', (err, origin) => { console.info('未知的错误发生了', err, origin); }); setTimeout(() => { console.info('在异常后执行的异步回调'); }, 0); xxx(); // 调用一个不存在的函数,将会报错 console.info('这行代码将不会被执行到!');

![捕获未知异常](捕获未知异常.png)

<img align='absmiddle' alt=':point_up_2:' class='emoji' src='/gitbook/gitbook-plugin-advanced-emoji/emojis/point_up_2.png' title=':point_up_2:' /> 这里调用了一个不存在的函数,则直接报错,并让程序直接异常退出,这个是常规node程序的一个运行过程,这里我们切忌
不能直接在这个回调事件中进行将程序自动重启,这 <img align='absmiddle' alt=':u6709:' class='emoji' src='/gitbook/gitbook-plugin-advanced-emoji/emojis/u6709.png' title=':u6709:' /> 可能会导致程序一些资源的异常,比如db的连接,文件的读写,我们应该是在
程序奔溃的时候,**让程序遵循其生命周期,而非强行控制**,:confused: 假如我们还是想要让程序在出现异常的时候,自动进行程序在异常
退出时自动重启,:six_pointed_star: 可通过外部程序来监控当前程序,让其当出现异常并且退出的情况下,**同步**记录异常信息后,自动重启
程序,简而言之,就是程序要退出了,我们就让它自动去退出,而不去干预,可以做的只是让其退出!!!

<img align='absmiddle' alt=':confused:' class='emoji' src='/gitbook/gitbook-plugin-advanced-emoji/emojis/confused.png' title=':confused:' /> <img align='absmiddle' alt=':u6709:' class='emoji' src='/gitbook/gitbook-plugin-advanced-emoji/emojis/u6709.png' title=':u6709:' /> 了`uncaughtException`还有这个`uncaughtExceptionMonitor`事件,这嘎嘎是用来作甚的吖?

<img align='absmiddle' alt=':point_right:' class='emoji' src='/gitbook/gitbook-plugin-advanced-emoji/emojis/point_right.png' title=':point_right:' /> 在发出`uncaughtException`事件或者通过调用process.setUncaughtExceptionCaptureCallback()安装程序的钩子函数之前,会调用`uncaughtExceptionMonitor`事件,
<img align='absmiddle' alt=':point_down:' class='emoji' src='/gitbook/gitbook-plugin-advanced-emoji/emojis/point_down.png' title=':point_down:' /> 在之前的例子加入几行代码:
```javascript
  // .......此处省略代码
  process.on('uncaughtExceptionMonitor', (err, origin) => {
    console.info('我是uncaughtExceptionMonitor事件,被调用了我!');
    console.info(origin);
  });
  // .......此处省略代码

在异常捕获之前捕获

  1. 警告(warning) :star: 警告事件并不是nodejs与javascript错误处理流程的一部分,它仅仅是检测到代码中可能会导致程序性能、错误、或者安全漏洞的不良编码行为时,所发出的警告,其目的是更好地为了程序的执行, 默认情况下的warning信息是通过普通的stderr来输出的比如咱们的控制台

     const process = require('node:process');
     const events = require('node:events');
     process.on('warning', warning => {
         console.info('警告警告:', warning);
     });
     const emitter = new events.EventEmitter();
     emitter.setMaxListeners(1);
     emitter.on('foo', () => {});
     emitter.on('foo', () => {});
    

    :point_up_2: 这里我们创建了一个设置了一个对警告的监听,假如正常运行程序的话,将会直接报错,因为这个是node环境所发出的,将异常信息进行输出! 警告退出异常 而如果追加了--no-warning参数给node的话,则将不会输出警告信息: 不带警告执行

  2. 进程通信(message与worker、disconnect) :star: 当创建了一个worker的时候,这个worker事件将会被回调; :star: 如果当前程序所在进程是通过IPC通道生成的,那么只要子进程接收到父进程使用childprocess.send()发出的消息,就会发出message事件 :star: 如果当前程序是用过IPC通道生成的,那么当IPC通道关闭的时候,disconnect事件将会被调用!

  3. 信号事件

属性成员

  1. 启动程序相关参数(execArgv、argv、argv0) :star: execArgv属于当node程序执行时候,传递给node的相关参数 :star: argv表示启动node程序时候,传递的命令行参数,但不包括execArgv :star: argv0表示启动node程序时的argv的第一个值,也就是argv[0]

:point_down: 是关于node命令行的一个组成 node命令行参数组成

const process = require('node:process');
console.info('以下是argv');
console.info(process.argv);
console.info('以下是argv0');
console.info(process.argv0);
console.info('以下是execArgv');
console.info(process.execArgv);

node命令行参数

  1. 程序运行时环境变量(env) :star: 程序运行时所在的环境变量用env表示,我们可以往这个env中进行一个临时变量的存储,目前以及以后的版本将只允许丢字符串的变量进来了,比如我们目前用的vue项目,我们可以从process.env.ENVIROMENT变量中获取当前程序的运行环境, 因为提前将变量进行了赋值操作!!!
const process = require('node:process');
console.info(process.env);
process.env.foo = 'bar';
console.debug(process.env);

process.env追加变量:point_up: 我们可以看出process.env允许我们将用户自定义变量,通过配置的方式传递进去,来达到动态地控制不同的变量执行不同的函数的过程,这里假如 :confused: :u6709: 这样子的一个场景: :point_right: 如果我们想要运行一个程序,然后该程序在开发阶段采用开发阶段的host,在生产阶段使用生产阶段的host,这一个过程采用命令行 :heavy_plus_sign: 脚本化参数的方式来实现,是否可以的呢? 答案是肯定的,我们可以通过从命令行参数中获取到用户传递进来的所有的变量,然后将对应的变量存储到process.env对象中,那么我们则可以在程序的运行过程中从env对象中获取,这也许就是crossenv库的工作原理吧!!!

实例函数

  1. exit,用于控制程序直接退出以怎样的一个退出状态码方式来退出,可用来标记是系统的退出还是自己编写的程序的异常退出,一般一旦执行,就直接等对应的beforeExit以及exit回调执行后来退出!
const process = require('node:process');

process.on('exit', () => {
    console.info('退出了');
});

process.exit(1);
console.info('exit后的同步调用');
setTimeout(() => {
    console.debug('设置了exitCode=1后的动作');
}, 500);

process.exit退出

:point_up_2: 上面这里再执行完成process.exit(1)之后,程序就处于等待退出状态,后续的其他的操作都会被抛弃掉!!! :star2: 一般情况下,我们没有必要自行去调用这个process.exit(),而是交由程序来自动的进行执行,并到了应该退出的时候,不编写对应的执行操作即可

  1. 抛出自定义异常(emitWarning)
    const process = require('node:process');
    process.emitWarning(warning, {
     type: '代表warnging的名字描述',
     code: '一个自定义的唯一code',
     ctor: '当warning是一个字符串的时候,该函数用来限制生成的执行堆栈',
     detail: '额外的异常描述字符串'
    });
    
    :confused: 通过自定义异常,可以实现一个全局的自定义异常捕获,可以将异常进行自定义,比如账号错误、密码错误、操作异常、数据库异常等等,然后通过对应的工厂来创建对应的自定义异常对象, 这里可以采用将异常进行自定义封装,然后在需要调用的时候,就抛出这个自定义异常,实现业务异常的统一处理。
const process = require('node:process');
process.on('warning', warning => {
    console.info(warning);
});
process.emitWarning("我的自定义异常", {
    code: 'myWarning',
    detail: '这个是来自xxx的异常'
});

自定义异常

  1. 拦截未捕获的异常(setUncaughtExceptionCaptureCallback与hasUncaughtExceptionCaptureCallback) :star: 当我们通过setUncaughtExceptionCaptureCallback方法来设置异常捕获的时候,原本的uncaughtException回调将不会被调用, 也就是它与回调方法基本上是属于互斥的状态!!

  2. 异步调用方法(nextTick) :star: 在经过之前的学习,我们已经知道关于node程序的一个事件循环以及事件消费的过程原理,这里通过process.nextTick的方式,往循环队列中添加一个 待被执行的回调,然后消费者自动从中获取事件来执行的过程。

nextTick发生在每一次事件循环之前,假如这个时候我们采用的在nextTick中不断的回调的话,那么这个时候js执行的CPU时间将会被它消耗殆尽! 记住了关于nextTick的一个触发机制,我们则可以在创建一个新的对象之后,立马安排它在资源可用的情况进行一个执行操作,这有点类似于前端的编程模式,原本需要获取到某个节点才进行对这个节点 执行某个操作,通过this.$nextTick的方式,来等待到资源准备好之后,触发这个动作!

:confused: 为什么要使用这个process.nextTick:question: :point_down: 一切都为了更及时地来使用资源,看下面的例子就 :u6709: 一个比较深刻的印象了

const EventEmitter = require('events');
class MyEmitter extends EventEmitter {
  constructor() {
    super();
    this.emit('event');
  }
}
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
  console.log('an event occurred!');
});

:stars: 一般情况下,我们创建一个自定义的事件监听器对象,可以通过继承于EventEmitter对象,然后来创建对事件的监听,假如我们想要 在一创建就想立马监听并触发一个响应动作的话,正常情况下,我们是new出一个对象之后,通过显式地调用emit来触发,而不能通过简单地在其构造函数中进行触发, 那么这里时候我们可以采用process.nextTick的方式,让系统告知程序,我资源准备好了,你可以触发事件了,通过nextTick将其进行一个包裹

  // ...这里省略相关的代码
  process.nextTick(() => {
      this.emit('event');
  });
  // ...这里省略相关的代码

exitCode一览

一般一个node正常执行的时候,其process.exitCode默认为0,然而有时候,程序在意想不到的情况下,出现了异常并退出了,这个时候,process.exitCode将 :u6709: 不同的取值,:point_down: 列举一下不同场景下的exitCode值以及其对应的含义:

exitCode值 异常名称 描述或场景
1 uncaughtFatalException 未捕获的致命异常,未能预先在程序中处理到的异常,将会导致该异常退出的情况,这个一般比较常见
2 Unused 没用过
3 Internal JavaScript Parse Error js解析错误,一般在开发编码阶段就会发生了,比较少见
4 Internal Javascript Evaluation Failure js内部引导错误,比较少见
5 Fatal Error v8引擎中出现的不可恢复的致命错误
6 Non-function Internal Exception Handler 函数内部异常,到时原本应该是函数来调用编程非函数调用
7 Internal Exception Handler Run-Time Failure 捕获到异常错误信息后,处理异常错误时又报了异常,编写处理异常代码有异常
8 Unused
9 Invalid Argument 原本函数调用应该传递的一个参数,但在实际调用的过程中没有传递
10 Internal JavaScript Run-Time Failure Internal JavaScript Parse Error
12 Invalid Debug Argument 传递了无效的调试参数
14 Snapshot Failure 启动v8引起快照,但由于某个原因导致其失败
>128 SignalExits 当nodejs接收到SIGKILL或者SIGHUP的致命信号时发出的

process总结

通过学习了关于process,可以更加深入的理解自己的程序,以及借助于process所提供的属性和api以及事件监听,更好地来编写自己的程序,从 异常的捕获、未知错误的抛出监听,到像linux中控制进程般来控制自己所编写的程序,像android中编写的继承于Application的方式来理解process的 生命周期,来编写更加健壮、稳定的代码,结合vue这种借助于webpack打包来实现的env共享,可以更好地来理解vue程序是如何做到共享运行时变量的!!

process运用场景思考

results matching ""

    No results matching ""