对 require('foo') 的多次调用可能并不会多次执行该模块的代码。 这是一个重要的功能。 使用它,可以返回“partially done”对象,从而允许根据依赖关系一层一层地加载模块,即使这样做可能会导致循环依赖。
如果要让某个模块在每次被加载时都去执行代码,则需要 exports 一个函数,并调用该函数即可。
模块缓存注意事项
模块是基于其解析出来的文件名进行缓存。根据调用模块的路径,被调用的模块可能会解析出不同的文件名(从node_modules文件夹加载)。如果解析出来的是不同的文件,它不保证每次 require('foo') 总是返回相同的对象。
另外,在不区分大小写的文件系统或操作系统上,不同的解析文件名可以指向相同的文件,但缓存仍将它们视为不同的模块,并将重新加载该文件多次。 例如, require('./ foo') 和 require('./ FOO') 返回两个不同的对象,而不管 ./foo 和 ./FOO 是否是同一个文件。
核心模块
Node.js有些模块被编译成二进制文件。 本文档中的其他部分将对这些模块进行更详细的描述。
核心模块在Node.js的源码 lib/ 文件夹中。
如果核心模块的模块标识传递给 require() ,则它们总是优先加载。 例如,即使有一个自定义模块叫 http ,我们去执行 require('http') 也将始终返回内置的 HTTP 模块,
循环引用
当循环引用 require() 时,返回模块可能并没有执行完成。
考虑这种情况:
a.js :
console.log('a starting'); exports.done = false; const b = require('./b.js'); console.log('in a, b.done = %j', b.done); exports.done = true; console.log('a done');
b.js :
console.log('b starting'); exports.done = false; const a = require('./a.js'); console.log('in b, a.done = %j', a.done); exports.done = true; console.log('b done');
app.js :
console.log('main starting'); const a = require('./a.js'); const b = require('./b.js'); console.log('in main, a.done = %j, b.done = %j', a.done, b.done);
当 app.js 加载 a.js 时, a.js 依次加载 b.js . 此时, b.js 尝试加载 a.js . 为了防止无限循环,将 a.js 导出对象的未完成副本返回到 b.js 模块。 b.js 然后完成加载,并将其导出对象提供给 a.js 模块。
当 app.js 加载了这两个模块时,它们都已经完成。 因此,该程序的输出将是:
$ node app.js main starting a starting b starting in b, a.done = false b done in a, b.done = true in main, a.done =true, b.done = true
模块包装器
在执行模块的代码之前,Node.js将使用一个函数包装器来将模块内容包裹起来,如下所示:
(function (exports, require, module, __filename, __dirname) { // 你的模块代码 });
通过这样做,Node.js实现了以下几点:
它将模块内部的顶级变量(定义为 var , const 或 let )的作用域范围限定为模块内部而不是全局。
它有助于给模块内部提供一些实际上只属于该模块的全局变量,例如:
module 和 exports 对象用来帮助从模块内部导出一些值
变量 __filename 和 __dirname 是当前模块最终解析出来的文件名和文件夹路径
module 对象签名
Object module { id: String, // 模块标识,为该模块文件在系统中的绝对路径 exports: Object, // 该模块的导出对象 parent: Object | undefined, // 引用该模块的父模块 filename: String | null, // 最终解析的文件名称, 与__filename相同。 loaded: Boolean, // 该模块是否已经加载 children: Array, // 改模块的引用列表 paths: Array // 模块加载路径 }
require 函数签名
Function require { [Function], // 函数体 resolve: Function, // 根据模块标识解析模块,返回绝对路径 main: undefined | Object, // 应用的主(main)模块 extensions: {'.js':Function, '.json':Function, '.node':Function}, cache: Object // 模块缓存,以模块的绝对路径为key }
您可能感兴趣的文章: