面试题答案
一键面试CommonJS模块加载器工作原理
- 模块定义:在CommonJS规范中,一个文件就是一个模块,每个模块都有自己独立的作用域。模块通过
exports
或module.exports
对象来暴露对外接口。例如:
// module.js
const localVar = 'This is a local variable';
function localFunction() {
return 'This is a local function';
}
exports.localVar = localVar;
exports.localFunction = localFunction;
- 加载过程:当使用
require
函数加载一个模块时,模块加载器首先会在文件系统中定位该模块文件。如果模块是核心模块(如fs
、http
等Node.js内置模块),会直接加载。对于自定义模块,会根据相对路径或绝对路径找到对应的文件。然后,模块加载器会将该模块文件中的代码包装在一个函数中执行,这个函数的参数包括exports
、require
、module
、__filename
、__dirname
等。例如:
// wrapper function
(function (exports, require, module, __filename, __dirname) {
const localVar = 'This is a local variable';
function localFunction() {
return 'This is a local function';
}
exports.localVar = localVar;
exports.localFunction = localFunction;
})(exports, require, module, __filename, __dirname);
- 依赖解析:模块中如果使用
require
引入其他模块,加载器会递归地加载这些依赖模块。只有当所有依赖模块都加载并执行完毕后,当前模块才会继续执行并返回。
Node.js环境下模块缓存实现
- 缓存机制:Node.js使用一个对象来缓存已经加载过的模块。这个缓存对象位于
require
函数的内部。当require
函数加载一个模块时,首先会检查缓存中是否已经存在该模块。如果存在,则直接从缓存中返回该模块的exports
对象,而不会重新执行模块代码。例如:
// main.js
const module1 = require('./module1');
const module2 = require('./module1');
// 这里module1只被加载和执行一次,第二次require直接从缓存中获取
- 缓存标识:Node.js根据模块的标识符(通常是文件路径)来标识模块。对于相同标识符的模块,只会加载一次。即使在不同的模块中多次
require
同一个模块,也只会执行一次模块代码,并将结果缓存起来供后续使用。例如,无论在多少个模块中require('./module.js')
,只要路径相同,都共享同一个缓存实例。 - 缓存清除:一般情况下,模块缓存会一直存在于进程的生命周期内。但是在某些特殊场景下,如测试环境中需要重新加载模块以确保测试的准确性,可以手动清除缓存。例如,在测试框架中可以通过修改
require
缓存对象来移除特定模块的缓存:
// 在测试代码中
delete require.cache[require.resolve('./module.js')];
这样再次require('./module.js')
时,就会重新加载该模块。