3.4 Node.js的模块系统
Node.js应用由模块组成,采用CommonJS模块规范。
每个JS文件就是一个模块,有独立的作用域。在一个文件中定义的变量、函数、类在其他文件都不可见。
// example.js function sum(a, b) { return a + b; }
上述代码中sum函数只有example.js中能调用,其他文件则不可以调用。
3.4.1 module和exports
1.基本用法
CommonJS规范规定,每个模块内部的变量module代表当前模块。
module是一个对象,module.exports是对外的接口。加载模块实际上是读取该模块的module.exports属性。
module.exports也是一个对象,所有需要导出的变量、函数、类都需要挂载到该对象上才能实现导出。
// example.js function sum(a, b) { return a + b; } module.exports.sum = sum;
为了方便起见,Node.js为每个模块提供了一个exports变量,在同一个模块中,module.exports和exports是恒等的(类型和值都相等)。因此在实际开发中,建议通过exports.xxx的形式导出变量、函数、类。
不建议更改exports的指向,否则模块将不能正常导出。
下列代码无法导出sum函数,因为exports由于重新赋值导致指向被更改。
// example.js exports.sum = function(a, b) { return a + b; } exports = 'Hello World';
如果模块只需要导出一个变量、函数、类,只能对module.exports进行赋值,对exports进行赋值达不到如期作用。
上面讲到module.exports是恒等于exports的,为什么对exports进行赋值达不到如期作用呢?
// a.js exports = 'hello world'; // b.js module.exports = 'hello world'; // c.js const hello = require('./a'); console.log(hello); // {} const hello2 = require('./b'); console.log(hello2); // hello world
2.原理解读
exports只是一个别名,类似于下面的代码:
var exports = module.exports;
当不对exports重新赋值时,exports指向不变,exports.xxx也会如期地添加到module.exports中。
当对exports重新赋值时,exports和module.exports关联就不存在了,修改exports不会对module.exports产生作用。
3.4.2 require
1.基本用法
CommonJS规定require用于加载模块文件。
require读取并执行一个JS模块,然后返回该模块的exports对象。如果模块未找到,则会抛出错误。
// math.js exports.sum = function(a, b) { return a + b; } // index.js const math = require('./math'); math.sum(1, 1);
2.加载规则
加载模块时模块扩展名为.js。也就是说下列代码是一样的:
const math = require('./math'); const math2 = require('./math2');
根据传入的参数,require会有不同的规则(以下规则无先后顺序):
(1)如果参数以'/'开头,则表示需要加载的是一个绝对路径的JS文件。如require('/home/xialei/math')将加载/home/xialei/math.js。
(2)如果参数字符串是以'./'开头,则表示需要加载一个相对路径的模块文件。如require('./math')将加载位于当前模块同目录下的math.js文件。
(3)如果参数不以'./'或'/'开头,则需要加载的是核心模块或者当前工作目录中node_modules下的模块。
(4)如果没有找到指定的模块文件,Node.js会尝试自动添加.js、.json、.node(编译后的二进制模块)后再去搜索。
(5)如果传入的参数解析之后是一个目录,Node.js会自动读取该目录下的package.json文件,根据main字段来加载真正的入口文件。如果该目录下没有package.json文件,则尝试加载index.js或index.node。
3.4.3 开发一个自定义模块
我们将开发一个与时间操作相关的函数模块来巩固本节所学,示例代码如下。
date.js:
index.js:
const date = require('./date'); const now = parseInt(Date.now() / 1000, 10); console.log(date.formatTime(now - 60)); console.log(date.formatTime(now - 600)); console.log(date.formatTime(now - 5400)); console.log(date.formatTime(now - 3600 * 23)); console.log(date.formatTime(now - 3600 * 24)); console.log(date.formatTime(now - 3600 * 24 * 3)); 在终端输入以下命令执行index.js: node index.js
输出如下:
刚刚 1小时内 3小时内 今天 1天前 Fri Oct 23 2019 18:07:45 GMT+0800 (中国标准时间)