为什么我的导出函数不是一个函数?

考虑下面的例子:

// bar.js const foo = require('./foo'); module.exports = function(args){ let f = foo(args) } // foo is not a function 

然后:

 // bar.js module.exports = function(args){ let f = require('./foo')(args) } // behaves as expected 

foo.js看起来像:

 const bar = require('./bar'); module.exports = function(args){ //same args as bar.js const foo = {}; foo.f1 = function(arg){ console.log("Hi") } return foo }; 

你正在处理循环依赖的问题。 我想foo.js看起来像这样:

 const bar = require('./bar'); module.exports = function(args) { console.log(args); }; 

让我们按照VM执行代码的步骤(假设你先加载foo.js ):

  1. 加载foo.js 使用A = {}初始化导出。
  2. 加载bar.jsB = {}初始化导出。 现在在bar.js
    1. bar.js需要输出foo.js (仍然是A = {} ),所以在bar.js中将foo设置为A = {} (这是问题!)。
    2. 用声明的函数replacebar.js的输出。
  3. 用声明的函数replacefoo.js的导出。

正如您所看到的,如果使用循环依赖关系,虚拟机无法跟踪foo.jsbar.js的导出。

在你的第二个例子中,当函数被调用时,你解决foo.js的输出。 那时候, foo.js的输出已经被这个函数替代了,而且它起作用了。


短版:这是发生了什么事。

  1. VM加载foo.js并使用对象A初始化foo.js的导出。
  2. VM加载bar.js并用对象B初始化bar.js的导出。
  3. VM将bar.js的variablesfoo设置为bar.js的导出,因此foo = A
  4. VM用函数Creplacebar.js的输出
  5. VM将foo.js的variablesbar设置为foo.js的导出,因此bar = C
  6. VM用函数Dreplacefoo.js的输出。

正如你可以再次看到的, bar.js的variablesfoo仍然是A ,即使它应该是D


你通常要避免使用循环依赖 。 如果真的有必要,那么不要replacemodule.exports ,而是将属性附加到对象上:

 module.exports.doBarAction = function(args){ let f = foo.doFooAction(args) }; 

这解决了这个问题,因为在运行时对象仍然是一样的。
但是,在大多数情况下,至less只要我们谈论CommonJS模块,最好摆脱循环依赖。