我试图理解这段代码是如何工作的:(这是 Eloquent Javascript 书中的练习。http://eloquentjavascript.net/10_modules.html#i_E/zWqBFdy8)
require.cache = Object.create(null);
function require(name) {
if (!(name in require.cache)) {
let code = readFile(name);
let module = {exports: {}};
require.cache[name] = module;
let wrapper = Function("require, exports, module", code);
wrapper(require, module.exports, module);
}
return require.cache[name].exports;
}
// Example:
// a.js
const {b} = require('./b');
console.log(b);
exports.a = 'a';
// b.js
const {a} = require('./a');
console.log(a);
exports.b = 'b';
书中指出:“许多模块系统只是禁止这种情况(循环依赖),因为无论您选择哪种顺序加载此类模块,都无法确保每个模块的依赖项在运行之前都已加载。”
解决方案是:“Require 在开始加载模块之前将模块添加到其缓存中。这样,如果在运行时进行的任何 require 调用尝试加载它,那么它已经是已知的,并且将返回当前接口,而不是再次开始加载模块。”
我无法理解模块加载顺序的含义 以及为什么我们不确定是否加载了依赖项,因为代码是同步的。最终我不确定我作为示例添加的两个模块中的代码如何交互。
我正在读第四版。请求功能与您的稍有不同。为了完成这项工作,我们需要三个文件:
loader.js
、a.js
、b.js
。 Loader 包含 require
函数并加载所有模块。
它看起来像这样:
// loader.js
import {readFileSync} from 'fs';
function require(path) {
if (!(path in require.cache)) {
let code = readFileSync(path, 'utf8');
let exports = require.cache[path] = {};
let wrapper = Function("require, exports", code);
wrapper(require, exports);
}
return require.cache[path];
}
require.cache = Object.create(null);
require('a.js');
// a.js
require('b.js');
console.log('finished loading a');
// b.js
require('a.js');
console.log('finished loading b');
当 A 被加载时,
require
函数首先将 A 添加到其缓存中。然后A模块需要B模块(此时A尚未完成加载)。当 B 开始加载时,它需要 A 模块,但 A 模块的 exports
已缓存,因此 require
函数从缓存中返回它 ({}) — 它不会再次加载。这个B加载完毕后,A模块中对require
的调用返回,A就完成加载了。
注意当B需要A时,A还没有加载完成。因此,如果 A 在
require
调用之后导出某些函数,则该函数尚未可供 B 使用,访问它可能会导致问题。但是,如果 A 在需要 B 之前先导出一个值,则 B 将有权访问该值。同样,B 导出的所有内容始终可以被 A 访问,因为 B 总是先于 A 完成加载。
// a.js
exports.aLog = () => console.log('this is a logging!');
const {bLog} = require('b.js');
bLog();
console.log('finished loading a');
// b.js
const {aLog} = require('a.js');
aLog();
exports.bLog = () => console.log('this is b logging!');
console.log('finished loading b');
// → this is a logging!
// → finished loading b
// → this is b logging!
// → finished loading a