模块系统
JavaScript 原生态是一个全局的世界。所有如 setTimeout、document 等这样在浏览器端使用的 API,都是全局定义的。当我们在 html 文档中使用 script 标签引入第三方模块时,会引入该模块暴露出来的全局变量,这会导致对全局命名空间的污染和命名冲突的问题。Node 采用了 CommonJS 模块化规范。该模块系统有三个核心的全局对象:require、module 和 exports。
绝对模块和相对模块
绝对模块是指 node 通过在其内部 node_modules 查找到的模块,或者 node 内置的如 fs 这样的模块。可以直接通过名字来 require 这个模块,无需添加路径名:var fs = require('fs')
,这里由于 fs 模块暴露了一系列操作文件系统的方法,所以在引入时用变量接收一下这个对象。有些模块没有暴露 API,则直接引入即可,无需用变量接收。
相对模块将 require 指向一个相对工作目录中的 Javascript 文件。在引入时需要添加相对路径名。
暴露 API
要让模块暴露一个 API 成为 require 调用的返回值,就要依靠 module 和 exports 这两个全局变量。
默认情况下,每个模块都会暴露出一个名为 exports 的空对象,我们可以在该对象中添加属性。例如:
module_a.cjs
exports.name = "john";
var number = 888;
exports.getName = function () {
return number;
};
index.cjs
const a = require("./module_a.cjs");
console.log(a.name); // john
console.log(a.getName()); // 888
当我们在 index.cjs 中 require 导入的时候,它会去自动查找特殊的全局对象 exports,并且把 require 函数的执行结果赋值给 a;
a 和 exports 指向同一个引用(引用地址相同);
由于我们在 exports 对象上添加了属性,所以在 a 对象也可以访问到这些属性;
exports 其实是对 module.exports 的引用,实际导出的是 module.exports,其在默认情况下是一个对象。如果默认导出该对象无法满足需求,则可以重写 module.exports。
person.cjs
module.exports = Person;
function Person(name) {
this.name = name;
}
index.cjs
const Person = require("./person.cjs");
var person = new Person("tom");
console.log(person.name); // tom
注意:如果多次使用 module.exports,则后面的会覆盖前面的导出对象。