为什么跨模块的相互依赖函数在运行时在节点 javascript 中评估为未定义

why do interdepedent functions across modules evaluate to undefined at runtime in node javascript

我有三个 javascript 个文件

moduleA.jsmoduleB.jsindex.js

moduleA.js

const {b1} = require('./moduleB');
const a1 = ()=>{
    console.log('a1 called')
};
const a2 = ()=>{
    console.log('a2 called');
    b1()
};

module.exports = {a1, a2};

moduleB.js

const {a1} = require('./moduleA');

const b1 = () => {
    console.log('b1 called');
    a1();
};
const b2 = () => {
    console.log('b2 called');
};

module.exports = {b1, b2};

index.js

const {a2} = require('./moduleA');
a2();

为什么调用 a2() 会在运行时抛出错误,因为 a2 在节点中是 undefined?我还没有在其他语言中看到这种类型的错误,例如 Java.

除了将 a2 放入另一个文件(例如 moduleC.js 并调用 moduleC.a2() 之外,是否有其他解决方案?

example code on codingrooms

当前输出为:

TypeError: a1 is not a function
    at b1 (... /moduleB.js:5:5)
    at a2 (... /moduleA.js:7:5)

预期输出为

// a2 called
// b1 called
// a1 called

附加信息

基于 Felix 提供的公认答案以及我对 Matt Frisbie 的 Professional JavaWeb Developers[=] 中 Modules 章节的理解67=]。我画这张图是为了帮助我理解 CommonJS 中的模块加载过程。我希望将来遇到这个问题的人会发现它和我一样有用。

如评论中所述,错误是a1undefined,而不是a2。原因是模块 A 和模块 B 之间存在循环依赖关系。

当模块 B 从模块 A 导入 a1 时,模块 A 的 exports 对象仍然是空的,即 a1 还不存在。

要解决此问题,您需要做两件事:

  • 延迟访问 a1 直到需要它(这是在评估所有模块之后)。
  • 不要覆盖初始导出对象,而是更新它。
const a = require('./moduleA');
//   ^^^ 

const b1 = () => {
    console.log('b1 called');
    a.a1();
//  ^^
};
const b2 = () => {
    console.log('b2 called');
};

module.exports = {b1, b2};
const {b1} = require('./moduleB');
exports.a1 = ()=>{
    console.log('a1 called')
};
exports.a2 = ()=>{
    console.log('a2 called');
    b1()
};

或者当然重构您的代码,使其不使用循环依赖。