模块化概念
模块化是指解决一个复杂问题时,自顶向下逐层把系统划分成若干模块的过程。对于整个系统来说,模块是可组合、可分解和可更换的单元。
在模块化时,要遵守固定的规则(模块化规范),把一个大文件拆成独立并互相依赖的多个小模块。
进行模块化拆分的好处:
- 提高了代码的复用性。
- 提高了代码的可维护性。
- 可以实现按需加载。
- 遵守同样的模块化规范,有利于降低沟通的成本,极大地方便了各个模块之间的相互调用,利人利己。
Node.js 模块化
Node.js中根据模块来源的不同,将模块分为了3大类,分别是:
-
内置模块:由Node.js官方提供,例如
fs
、path
、http
等。 -
自定义模块:用户自定义的模块。在Node.js中,用户创建的每个
.js
文件,都属于自定义模块。在将
.js
文件加载为模块时,需要指定其路径(.js
后缀可以省略,也可以加上):const myModule = require('./custom')
-
第三方模块:由其他用户开发的模块,并非官方提供或用户创建的模块。使用第三方模块前需要先进行下载(例如使用
npm
安装的模块)。
注:使用
require()
方法加载模块时,会同时执行被加载模块中的代码。
模块作用域
模块作用域是指在模块中定义的变量、方法等成员,只能在当前模块内被访问。
模块作用域可以防止全局变量污染的问题。
CommonJS 模块化规范
Node.js遵循了CommonJS模块化规范,CommonJS规定了模块的特性和各模块之间相互依赖的方式。
CommonJS规定:
- 每个模块内部,
module
变量代表当前模块。 module
变量是一个对象,它的exports
属性(即module.exports
)是对外的接口。- 加载某个模块,其实是加载该模块的
module.exports
属性。require()
方法用于加载模块。
Module 对象
module
对象存放了当前模块中的基本信息。在一个空的js
文件中打印module
对象的结果如下:
Module {
id: '.',
exports: {},
parent: null,
filename: '/root/study-js/modularization/module-object.js',
loaded: false,
children: [],
paths: [
'/root/study-js/modularization/node_modules',
'/root/study-js/node_modules',
'/root/node_modules',
'/node_modules'
]
}
module
对象的属性:
filename
:当前模块文件的全路径名。exports
:向外共享成员、接口。
向外共享模块内成员
在自定义模块时,可以使用module.exports
对象,将模块内的成员向外部的其他模块共享,供外界使用。在外界使用require()
方法导入模块时,require()
方法提供的就是module.exports
所指向的对象。
为module.exports
对象挂载成员时,有两种方式:
-
为
module.exports
对象创建成员:// 在 module.exports 对象上挂载属性 module.exports.username = 'Linner' // 在 module.exports 对象上挂载方法 module.exports.hello = function () { console.log('Hello Orther Module!'); } // 挂载常量属性 const COLOR = 'black' module.exports.COLOR = COLOR
-
定义一个
module.exports
对象:// 定义 module.exports 对象 module.exports = { username: 'Linner', hello () { console.log('Hello Orther Module!'); }, COLOR: 'black' }
使用这两种方式来挂载,区别不大,可以在其它模块中打印输出进行观察(假设创建的.js
文件名为module-exports.js
):
const me = require('./module-exports');
// 打印其他模块的 module.exports
console.log(me);
向外共享模块作用域中的成员,除了使用module.exports
对象外,还可以使用exports
对象。默认情况下,exports
和moudle.exports
指向的是同一个对象。最终共享的结果,是以module.exports
指向的对象为准。
在exports-object.js
中编写:
exports.username = 'Linner'
exports.age = null
exports.hello = function () {
console.log('Hello!');
}
// 验证 exports 和 module.exports 指向的是否为同一个对象
console.log('exports', exports);
console.log('module.exports', module.exports);
console.log('Are they equal?', exports === module.exports);
/**
* 如果直接为 exports 对象赋值
* 此时 exports 和 module.exports 指向的就不是同一个对象了
*/
exports = {
username: 'Zhangsan',
age: '20'
}
console.log('Assign a value to the exports:');
console.log('exports', exports);
console.log('module.exports', module.exports);
console.log('Are they equal?', exports === module.exports);
然后在test-exports.js
中进行测试:
const eo = require('./exports-object');
console.log("The module.exports in exports-object:", eo);
运行test-exports.js
输出结果如下:
exports { username: 'Linner', age: null, hello: [Function] }
module.exports { username: 'Linner', age: null, hello: [Function] }
Are they equal? true
Assign a value to the exports:
exports { username: 'Zhangsan', age: '20' }
module.exports { username: 'Linner', age: null, hello: [Function] }
Are they equal? false
The module.exports in exports-object: { username: 'Linner', age: null, hello: [Function] }
在exports-object.js
中添加新的代码:
/* 省略其它代码... */
module.exports = {
username: 'Lisi',
age: '23',
gender: 'man'
}
console.log('Assign a value to the module.exports:');
console.log('exports', exports);
console.log('module.exports', module.exports);
console.log('Are they equal?', exports === module.exports);
再次运行test-exports.js
,输出结果如下:
exports { username: 'Linner', age: null, hello: [Function] }
module.exports { username: 'Linner', age: null, hello: [Function] }
Are they equal? true
Assign a value to the exports:
exports { username: 'Zhangsan', age: '20' }
module.exports { username: 'Linner', age: null, hello: [Function] }
Are they equal? false
Assign a value to the module.exports:
exports { username: 'Zhangsan', age: '20' }
module.exports { username: 'Lisi', age: '23', gender: 'man' }
Are they equal? false
The module.exports in exports-object: { username: 'Lisi', age: '23', gender: 'man' }
总结:如果对exports
或module.exports
只进行添加成员对象的操作,不直接给它们赋值,那么exports
和module.exports
指向的始终都是同一个对象。如果对exports
或module.exports
进行了赋值操作,那么exports
和module.exports
指向的就不是同一个对象,但是向外共享的成员还是以module.exports
为准。
注:为了防止混乱,不要再同一个模块中同时使用
exports
和module.exports
。建议使用module.exports
。
评论