前言
最近一直在写大论文,前端的知识看得有点少,抽空看一看,以免生疏。本篇主要介绍关于NodeJS中基于CommonJS规范的模块,文中大量内容摘取自阮一峰及廖雪峰两位大神的博客,整合加入了一些个人理解。
NodeJS中的模块
模块对外暴露变量:
|
|
模块引用其他模块暴露的变量:
|
|
CommonJS模块规范
基本内容
每个JS文件都是一个模块,内部各自使用的变量名和函数名互不冲突,包括全局变量。变量为当前模块的私有变量。
若想在多个文件分享变量,需定义为global对象的属性。
|
|
一般不推荐全局写法。
CommonJS规范规定,每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外输出的接口。其他文件加载某个模块,其实是加载该模块的module.exports属性。
Node保存了所有导入的module,使用require()获取module时,Node找到对应的module,将改module的exports变量返回,从而获得模块输出。
加载规则
require
命令用于加载文件,后缀名默认为.js
,可省略。
|
|
参数字符串不是绝对路径和相对路径时,模块将按照下列查找顺序进行查找:内置模块、全局模块、当前模块。如果指定的模块文件没有发现,Node会尝试为文件名添加.js、.json、.node后,再进行搜索。
require发现参数字符串指向一个目录以后,会自动查看该目录的package.json文件,然后加载main字段指定的入口文件。如果package.json文件没有main字段,或者根本就没有package.json文件,则会加载该目录下的index.js文件或index.node文件。
若想获得require命令加载的确切文件名,使用require.resolve()
命令。
特性
- 所有代码都运行在模块作用域,不会污染全局作用域。
- 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
- 模块加载的顺序,按照其在代码中出现的顺序。
- 模块中输入的变量是被输出模块的值的拷贝。即一旦输出一个值,模块内部的变化就不再影响该值。
模块实现原理
在浏览器中,不同JS文件中的同名全局变量会互相影响。JS语言本身没有模块机制保证不同模块可以使用相同变量名。
解决办法:使用闭包,将JS代码用一个函数包装,是该段代码的所有“全局变量”成为函数内的局部变量。
例:
|
|
修改为:
|
|
NodeJS就是利用JS语言函数式编程的特性,实现了模块的隔离。
module.exports vs exports
为了方便,Node为每个模块提供一个exports变量,指向module.exports。这等同在每个模块头部,有一行这样的命令: var exports = module.exports;
|
|
不能直接对exports赋值,指向其他变量,因为Node提供的exports和module.exports实际是同一个变量,并且初始化为空对象{}。当对exports直接赋值时,等于切断了exports与module.exports的联系。
因此,如果要输出的是一个函数或数组,只能给module.exports赋值,给exports赋值时无效的。
Module对象
Node内部提供一个Module构建函数。所有模块都是Module的实例。
每个模块内部,都有一个module对象,代表当前模块。包括以下属性:
- module.id 模块的识别符,通常是带有绝对路径的模块文件名。
- module.filename 模块的文件名,带有绝对路径。
- module.loaded 返回一个布尔值,表示模块是否已经完成加载。
- module.parent 返回一个对象,表示调用该模块的模块。
- module.children 返回一个数组,表示该模块要用到的其他模块。
- module.exports 表示模块对外输出的值。
如果在命令行下调用某个模块,比如node something.js
,那么module.parent
就是null
。如果是在脚本之中调用,比如require('./something.js')
,那么module.parent
就是调用它的模块。利用这一点,可以判断当前模块是否为入口脚本。
模块缓存
所有缓存的模块保存在require.cache之中,
删除模块的缓存:
|
|
注意,缓存是根据绝对路径识别模块的,如果同样的模块名,但是保存在不同的路径,require命令还是会重新加载该模块。
模块的循环加载
如果发生模块的循环加载,即A加载B,B又加载A,则B将加载A的不完整版本。
|
|
执行结果如下:
|
|
未完待续
参考文章
- 阮一峰,CommonJS规范
- 廖雪峰,JavaScript教程-Node.js-模块