NodeJS 运行环境

NodeJS 运行环境包含 CommonJS 模块规范、global 全局对象、process 当前进程

CommonJS 模块规范

理解 CommonJS 模块前先来看一个简单的例子:

创建一个 run.js 文件

console.log("This is a test");

之后通过 NodeJS 的 debug 调试工具可以看到

在这里插入图片描述
NodeJS 自动给我们创建了一个函数,将我们的代码放置在函数内部,其中 exports 代表我们需要向外暴露的模块或接口;require 表示需要依赖别的模块时调用的 function;module 代表模块本身。

由此我们可以总结出 CommonJS 的几条特性:

1、每个文件都是一个模块,有自己的作用域(每个文件有且只能有一个模块,因为 NodeJS 将其代码放在函数体内,所有都有其自己的作用域)

2、在模块内部 module 变量代表模块本身

3、module.exports 属性代表模块对外的接口

我们来看一个例子:

创建一个 cusmod.js 文件

console.log("this is a module"); 
// 定义一个变量
let testVar = 100;
// 定义一个函数
function test(){
    console.log(testVar);
};
// 将变量与方法暴露出去以便别的文件引用
module.exports.testVar = testVar;
module.exports.test = test;

既然变量与方法已经暴露出去,那么一定会在另一个文件中接收它,那么就有引出了 require 函数,我们先来看一下 require 函数的规则:

1、/ 表示绝对路径,./ 表示相对于当前文件的路径
2、支持 js、json、node 拓展名,不写依次尝试(如果引用的路径不写扩展名,则会依次查找,如果找不到就会报错)
3、不写路径则认为是 node 自带的模块或者是各级 node_modules 内的第三方模块

现在我们新建一个 require.js 去引入 cusmod.js 文件

// 使用 require 引入 cusmod.js 文件(不写拓展名则会依次查找)
let mod = require("./cusmod);
// 打印 cusmod.js 中定义的 testVar
console.log(mod.testVar)        
// 调用 cusmod.js  中定义的 test 方法
mod.test(); 

我们可以看到其输出结果为:


此处不仅将我们暴露的东西打印出来了,还将 cusmod.js 中定义的 console 也打印出来了,由此可以看出在 cusmod.js 中一些没有被暴露出的输出也会被打印出来,所以对于一些不想让用户看见或者不想暴露出来的东西,我们最好将其封装到方法中,等待调用时再获取。

让我们再来看看另一个例子:

创建一个 cache.js 文件

// 引入两次 cusmod.js 文件
require("./cusmod);
require("./cusmod);   

看一看输出的结果:
在这里插入图片描述
我们可以看到虽然引入了两次 cusmod.js 文件,但是只调用了一次,这就引出了 require 的第一条特性即 module 被加载的时候执行,加载后缓存。

我们再来看看 require 的另一个特性

还是先看一个例子:

创建一个 modA.js 文件

module.exports.test = "A";
let modB = require("./modB");
console.log("modA"+modB.test);
module.exports.test = "AA";

再创建一个 modB.js 文件

module.exports.test = "B";
let modB = require("./modA");
console.log("modB"+modA.test);
module.exports.test = "BB";

最后再创建一个 main.js 文件

let modA = require("./modA");
let modB = require("./modB");
console.log(modA.test);
console.log(modA.test);

最后的输出结果如图所示:

在这里插入图片描述
我们根据结果进行分析

首先,main.js 中调用了 modA.js 文件,当 modA.js 执行到 let modB = require("./modB") 这句时会去调用 modB.js 文件,当 modB.js 执行到 let modB = require("./modA") 这句时就形成了对 modA.js 的循环加载,此时当 modB.js 执行到 console.log(“modB”+modA.test)这句时只会输出 modA.js 中已经执行的部分,所以打印出 “modB:A”;当 modB.js 执行完之后又会回到 modA.js 中,而此时 modB.js 中 test 已经变成 BB ,所以输出的结果为 “modA:BB”;这样 main.js 中的第一句就执行完毕了,而由于 “module 被加载的时候执行,加载后缓存”的特性,第二句将不会执行。

综上实例,我们可以得出 require 的两条特性:

1、module 被加载的时候执行,加载后缓存
2、一旦出现某个模块被循环加载,就只输出已经执行的部分,还未执行的部分不会输出

最后,我们再来看一个问题:

还是先看这张图

NodeJS 给我们创建的函数的参数中有一个 exports,另一个参数 module 是一个对象其中也包含这一个 exports,那么它们两个有什么区别呢?

实际上,NodeJS 在创建函数之前有这么一句话"const exports = module.exports",将 module.exports 赋值给了 exports,也就是说它们在某种意义上是相等的,但是有一种情况只可以使用 module.exports 而不能使用 exports,让我们来看下面这个例子:

exps.js

exports.test = 100;

main.js

const mod = require("./exps.js");
console.log(mod.test);

其输出结果为:

在这里插入图片描述
这是显而易见的。

但是我们将其改变一下,如下所示:

exps.js

exports = {
    a : 2,
    b : 3,
    test : 100 
}

expamain.js 保持不变其输出的结果就会变成:

在这里插入图片描述
由此可见,在使用 exports 时,我们可以向其中添加属性,此时 exports 与 module.exports 是一样的,但是如果需要赋值为对象时,我们只能使用 module.exports ,因为使用 exports 会改变 exports 的指向使其不再指向 module.exports 而是指向新赋值的对象,从而导致 exports 不再与 module.exports 相等

global

global 与浏览器中的 window 一样,在浏览器中我们将一些常用的属性放入 window 中以方便使用,而在服务器中则将常用的属性放入了 global 中。

在 global 中的一些属性:

CommonJS

Buffer、process、console

timer

这些属性会在后面做详细了解,我们现在只来看一个例子初步理解一下 global

global.js

const testVar = 1000;

main.js

const mod = require("./global");
console.log(testVar);

此时的输出结果为:
在这里插入图片描述
可以看出 testVar 是未定义的。

我们稍微改变一下:

global.js

slobal.testVar = 2000;

main.js

const mod = require("./global");
console.log(testVar);

这次的结果为:
在这里插入图片描述
这次结果输出来了,没有报错,说明 testVar 已经添加到全局了

process

process 顾名思义就是进程的意思,它其中包含了许多跟进程执行相关的事件和方法,先来看几个常用的比较重要的对象:

argv – 表示启动 process 时所有的参数,返回的是一个数组

我们举个例子来看一下 argv 所返回的东西

argv.js

const {argv} = process;   // ES6 语法引入 process 中的 argv 对象
// 循环遍历 argv 返回的数组
argv.forEach((item) => {
    console.log(item);
});

其输出结果为:
在这里插入图片描述
显而易见,输出的第一个是 NodeJS 所在的位置,第二个当前执行的文件所在的位置。

除此之外,我们还可以添加参数,如图所示:
在这里插入图片描述
其输出的结果就会连参数也输出出来。

execArgv – 获取调用 node 所传入的一些特殊参数

修改一下 argv.js

const {execArgv} = process; 
console.log(execArgv);

其输出结果为:

在这里插入图片描述
可以看出 node 命令后传入的参数被打印出了。

execPath – 获取 node 所在的目录

再来修改一下 argv.js

const {execPath} = process; 
console.log(execPath);

其输出结果为:

在这里插入图片描述
可以看出其打印的结果就是 NodeJS 所在的目录,其实也就是 argv 返回的数组的第一个。

cwd – cwd 是一个方法,获取当前项目所在的路径

再来修改一下 argv.js

const {cwd} = process; 
console.log(cwd());

其输出结果为:

在这里插入图片描述

env – 获取一些与环境相关的信息

env.js

const {env} = process; 
console.log(env);

其输出结果为:

在这里插入图片描述
这就是获取的一些与环境有关的信息。

timer – 包括一些延时调用的命令,setImmediate、setTimeout 以及 process.nextTick

首先来看个例子:

timer.js

setImmediate(() => {
    console.log("setImmediate");
});

setTimeout(() => {
    console.log("setTimeout");
},0);

process.nextTick(() => {
    console.log("nextTick");
});

其输出结果为:

在这里插入图片描述
我们可以看出 process.nextTick 最先输出,setImmediate 最后输出,setTimeout 在它们中间输出。

事实上,setImmediate 是延时调用,它会在下一个事件队列开始时去执行,相当于一个异步,而 process.nextTick 是在当前队列中的东西都执行完后再执行它,因此它比 setImmediate 执行的早,setTimeout 的执行时间则是位于它们中间。

你可能认为 setImmediate 与 process.nextTick 没有什么区别,但事实并非如此,process.nextTick 是在当前事件队列的最后执行,可如果在其中循环调用 process.nextTick 就可能导致程序不能继续向后执行,一直执行 process.nextTick,因此在大部分情况下,我们都使用 setImmediate 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值