NodeJS 之模块化
- 参考
- 描述
- 模块化
- 优点
- 分类
- 加载
- 模块作用域
- 优点
- 自定义模块
- module
- module.exports
- exports 与 module.exports
- 示例
- 示例一
- 示例二
- 示例三
- 建议
- CommonJS
- 规范
- CommonJS
参考
项目 | 参考 |
---|---|
搜索引擎 | Bing |
哔哩哔哩 | 黑马程序员 |
描述
项目 | 描述 |
---|---|
操作系统 | Windows 10 专业版 |
NodeJS | 18.13.0 |
模块化
模块化即将完成一个项目所需要的不同功能按照一定的规则分别封装在不同的文件中,按照自己的需求导入需要的文件。
优点
- 复用性
模块为开发者提供了接口,同一个模块既可以被这个人使用,也可以被另一个人使用。为实现某一个功能可以导入实现了该功能的文件而不需要从零开始实现该功能。 - 可维护性
在项目的某个功能出现问题后,我们可以直接到实现该功能的模块中查找错误,而不需要在整个项目的所有文件中查找错误,提高了程序的可维护性。 - 按需加载
项目需要实现哪个功能,你只需要将对应的模块进行导入即可。
项目不需要哪个功能,你不将该模块进行导入即可。
分类
NodeJS 中的模块按照其来源可以划分为如下三种:
- 内置模块
内置模块由 NodeJS 官方提供,你不需要对其进行获取(下载及安装)即可直接使用。 - 自定义模块
每一个 JavaScript 文件都可以作为模块使用,但该模块能否发挥作用就需要看你有没有遵守模块的开发规范了。 - 第三方模块
由第三方开发的模块,在使用第三方模块时,你需要对其进行下载及安装(通过 JavaScript 包管理工具完成该操作)。
加载
在 NodeJS 中,加载一个模块的语法为:
require('此处为需要导入的模块名称或其所在的路径');
注:
- 如果你导入的模块为自定义模块,你需要为 require() 指定自定义模块所在的路径,即使该模块位于需要导入模块的文件所在的目录之下。
- 如果你导入的模块为 NodeJS 提供的内置模块,你仅需向 require() 函数提供该模块的名称即可。
模块作用域
模块作用域与函数类似,定义在模块中的函数、变量等成员仅能被该模块中的其他成员所访问,导入该模块的文件无法直接访问。这种模块级别的限制,称为模块作用域。
优点
模块作用域的成员仅当前模块中的其他成员可以访问,可以避免出现变量污染的情况发生。
举个栗子
从终端打印的错误信息 ReferenceError: username is not defined 可以了解到,我们在 index.js 文件中不能直接访问模块中的成员变量 username 。
这样设计可以避免 index.js 文件中定义的 username 变量被模块 target.js 中的 username 覆盖,导致变量被污染。
自定义模块
module
每一个 JavaScript 文件中都包含了 module 对象,该对象包含了当前模块的信息。我们可以尝试将该对象打印在终端中:
console.log(module);
打印结果:
Module {
id: '.',
path: 'C:\\Users\\36683\\TwoMoons\\WWW\\MoonLight\\src',
exports: {},
filename: 'C:\\Users\\36683\\TwoMoons\\WWW\\MoonLight\\src\\index.js',
loaded: false,
children: [],
paths: [
'C:\\Users\\36683\\TwoMoons\\WWW\\MoonLight\\src\\node_modules',
'C:\\Users\\36683\\TwoMoons\\WWW\\MoonLight\\node_modules',
'C:\\Users\\36683\\TwoMoons\\WWW\\node_modules',
'C:\\Users\\36683\\TwoMoons\\node_modules',
'C:\\Users\\36683\\node_modules',
'C:\\Users\\node_modules',
'C:\\node_modules'
]
}
module.exports
在 module 对象中存在另一个对象 exports ,该对象可用于将模块中的成员共享到其他文件中去。要共享某个成员,你仅需要将该成员定义为 module.exports 对象的方法或属性即可。
举个栗子:
打印结果:
RedHeart
从上述内容可以做出如下理解:
在 NodeJS 中使用 require() 函数导入某个模块,实际上导入的是 module.exports 对象。
exports 与 module.exports
NodeJS 还为开发者提供了 exports 对象,exports 与 module.exports 指向同一个对象,当 exports 与 module.exports 指向不同的对象时,被导出的对象为 module.exports 。
示例
exports 与 module.exports 的关系可以通过如下示例来加深理解。由于发生变化的文件仅为 target.js 文件,所以我将在此展示 index.js 中的内容:
// 尝试导入 target.js 模块
const target = require('./target.js');
console.log(target);
示例一
module.exports = {
username: 'RedHeart'
}
exports.username = 'TwoMoons';
打印结果:
{ username: ‘RedHeart’ }
分析:
在该示例中,我们首先使用赋值语句将 module.exports 指向了一个新的对象,而 exports 仍指向原对象。当 exports 与 module.exports 指向不同的对象时,被导出的对象为 module.exports 。
示例二
module.exports.age = 18;
exports.username = 'TwoMoons';
打印结果:
{ age: 18, username: ‘TwoMoons’ }
分析:
在该示例中, exports 与 module.exports 指向同一个对象。
使用 exports 与 module.exports 为该对象添加属性或方法时,同名属性或方法将被后一个赋值语句所覆盖;非同名属性或方法将共存在该对象中。
示例三
exports = {
username: 'RedHeart'
}
module.exports = exports;
module.exports.age = 18;
打印结果:
{ username: ‘RedHeart’, age: 18 }
分析:
我们通过赋值语句改变了 exports 指向的对象,但在后面,我们又通过 module.exports = exports; 使两者指向同一个对象。因此,导出的对象是我们期望的内容。
建议
为避免使用混乱,请不要在同一模块中同时使用 exports 与 module.exports ,尽量仅使用 module.exports 来对导出的对象进行设置。
CommonJS
规范
JavaScript 设计之初是没有考虑到模块化的,直到 ES6(2015) 才推出了自己的模块化方案。
在此之前,为了让JavaScript支持模块化,涌现出了很多不同的模块化规范:AMD、CMD、CommonJS 等。
CommonJS
NodeJS 遵循 CommonJS 规范。CommonJS 规定:
- 在每个模块内部,module 对象代表当前模块。
- module 是一个对象,它的 exports 属性是对外的接口。
- 加载某个模块,其实是加载该模块的 module.exports 属性。
- 使用 require() 来实现模块的加载。