为什么我的导出函数不是一个函数?
考虑下面的例子:
// 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
):
- 加载
foo.js
使用A = {}
初始化导出。 - 加载
bar.js
用B = {}
初始化导出。 现在在bar.js
:-
bar.js
需要输出foo.js
(仍然是A = {}
),所以在bar.js
中将foo
设置为A = {}
(这是问题!)。 - 用声明的函数replace
bar.js
的输出。
-
- 用声明的函数replace
foo.js
的导出。
正如您所看到的,如果使用循环依赖关系,虚拟机无法跟踪foo.js
中bar.js
的导出。
在你的第二个例子中,当函数被调用时,你解决foo.js
的输出。 那时候, foo.js
的输出已经被这个函数替代了,而且它起作用了。
短版:这是发生了什么事。
- VM加载
foo.js
并使用对象A
初始化foo.js
的导出。 - VM加载
bar.js
并用对象B
初始化bar.js
的导出。 - VM将
bar.js
的variablesfoo
设置为bar.js
的导出,因此foo = A
- VM用函数
C
replacebar.js
的输出 - VM将
foo.js
的variablesbar
设置为foo.js
的导出,因此bar = C
- VM用函数
D
replacefoo.js
的输出。
正如你可以再次看到的, bar.js
的variablesfoo
仍然是A
,即使它应该是D
你通常要避免使用循环依赖 。 如果真的有必要,那么不要replacemodule.exports
,而是将属性附加到对象上:
module.exports.doBarAction = function(args){ let f = foo.doFooAction(args) };
这解决了这个问题,因为在运行时对象仍然是一样的。
但是,在大多数情况下,至less只要我们谈论CommonJS模块,最好摆脱循环依赖。