您的位置 首页 java

「Node学习笔记」Node.js的模块实现及编译

前言

计算机程序 的开发过程中,随着程序代码越写越多,在一个文件里代码就会越来越长,越来越不容易维护。

为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式。在 Node 环境中,一个 .js 文件就称之为一个模块(module)。

「Node学习笔记」Node.js的模块实现及编译

一、使用模块的好处

使用模块最大的好处是大大提高了代码的可维护性。其次,编写代码不必从零开始。当一个模块编写完毕,就可以被其他地方引用。我们在编写程序的时候,也经常引用其他模块,包括 Node 内置的模块和来自第三方的模块。

使用模块还可以避免函数名和变量名冲突。相同名字的函数和变量完全可以分别存在不同的模块中,因此,我们自己在编写模块时,不必考虑名字会与其他模块冲突。

二、Node的模块实现

在 Node 中引入模块,需要经历如下三个步骤:

  • 路径分析
  • 文件定位
  • 编译执行

在 Node 中,模块分为两类:

第一类是 Node 自身提供的模块,称为 核心模块 :fs、http等,就像 java 中的 jdk 提供的核心类一样。

第二类是用户编写的模块,称为 文件模块

核心模块 部分在 Node 源代码的编译过程中,编译进了 二进制 执行文件。在 Node 进程启动时,核心模块就被直接加载进内存,所以这部分的模块引入时,文件定位和编译执行这两个步骤可以省略掉,并且在路径分析中优先判断,所以它的加载速度是最快的。

文件模块 在运行时动态加载,需要完整的路径分析,文件定位,编译执行过程,加载速度比核心模块慢。

以上介绍了模块加载过程和模块的类别划分,下面我们来看看 Node 加载模块的具体过程,如下图:

「Node学习笔记」Node.js的模块实现及编译

Node 为了优化加载模块的速度,也像浏览器一样引入了缓存,对加载过的模块会保存到缓存内,下次再次加载时就会命中缓存,节省了对相同模块的多次重复加载。模块加载前会将需要加载的模块名转为完整路径名,查找到模块后将完整路径名保存到缓存,下次再次加载该路径模块时就可以直接从缓存中取得。

在 require 加载模块时,require 参数的标识符可以以文件类型结尾 require(“./test.js”),也可以省略文件类型 require(“./test”)。

对于省略类型的第二种写法,Node 首先会认为它是一个 .js 文件,如果没有查找到该 js 文件,然后会去查找 .json 文件,如果还没有查找到该 json 文件,最后会去查找 .node 文件,如果连 .node 文件都没有查找到,就该抛出异常了。

Node 在执行 require 加载模块时是 线程 阻塞的,大家都知道 Node 是单线程执行的,如果长期阻塞的话系统其它任务就得不到执行了,所以为了加快 require 模块的加载,如果不是 .js 文件的话,在 require 的时候就把文件类型加上,这样 Node 就不会再去一一尝试了。

三、模块编译

在 Node 中,每个文件模块都是一个对象,具体定义如下:

「Node学习笔记」Node.js的模块实现及编译

编译和执行是引入文件模块的最后一个阶段。

定位到具体的文件后,Node 会新建一个模块对象,然后根据路径载入并编译。

对于不同的文件扩展名,其载入方法也有所不同,具体如下:

  • .js文件:通过 fs 模块同步读取文件后编译执行。
  • .json文件:通过 fs 模块同步读取文件后,用 JSON.parse() 解析返回结果。
  • .node文件:这是用 C/C++ 编写的扩展文件,通过 dlopen () 方法加载最后编译生成的文件。
  • 其余扩展名的文件:它们都被当作 .js 文件载入。

每一个编译成功的模块都会将其文件路径作为索引缓存在 Module._cache 对象上,以提高二次引入的性能。

我们都知道,在浏览器中编写的 js 文件,如果变量定义不是在函数或对象内,就会存在污染 全局变量 的情况,例如下面这种方式定义的变量:

 < script >

    var a = 'test';
</script>
  

等同于 window.a = ‘test’;

但我们在 Node 中的每个 .js 模块内并没有做任何其它处理,定义的变量怎么就不会污染全局环境了呢?还有在 Node 的模块内,怎么就可以直接使用module、require、exports、filename、 dirname等对象呢?

事实上,在编译的过程中,Node 对获取的 JavaScript 文件内容进行了头尾包装。

例如我们一开始写的 DateUtil.js,如下:

「Node学习笔记」Node.js的模块实现及编译

编译包装后的文件是下面这样的:

「Node学习笔记」Node.js的模块实现及编译

这样每个模块之间就进行了作用域隔离。

以上就是本期关于 Node.js 模块实现的分享,希望能给大家带来帮助。

下期给大家分享更多实战中的点滴,如果大家对此感兴趣,欢迎各位关注、留言,大家的支持就是我的动力!

文章来源:智云一二三科技

文章标题:「Node学习笔记」Node.js的模块实现及编译

文章地址:https://www.zhihuclub.com/193505.shtml

关于作者: 智云科技

热门文章

网站地图