为何Coffeescript中C访问B的静态属性为undefined而A访问正常?
为什么循环依赖下类B的静态属性在C中访问是undefined,而在a中是1?
代码示例
a.coffee
B = require './b' C = require './c' console.log B.someStaticVar C.checkB()
b.coffee
C = require './c' class B @someStaticVar: 1 module.exports = B;
c.coffee
B = require './b' class C @checkB: -> console.log B.someStaticVar module.exports = C
执行结果
执行coffee a.coffee后输出:
$ coffee a.coffee 1 undefined
问题原因:循环模块依赖的加载顺序问题
这本质是Node.js(CoffeeScript编译后运行在Node环境)的模块循环加载机制导致的,咱们一步步拆解加载执行的全过程:
- 启动
a.coffee,Node开始加载模块a:- 第一步加载
./b,进入b的模块执行流程。
- 第一步加载
- 加载
b模块时,首先执行C = require './c',转而加载c模块:- 在
c模块里,第一行是B = require './b'——这时候b模块还没执行完(它刚触发了c的加载,还没到定义class B和赋值module.exports = B的步骤)。 - Node的模块加载规则是:如果请求加载的模块正在加载中,就直接返回该模块当前的
module.exports值。此时b的module.exports还是默认的空对象(因为还没被赋值),所以c里的B变量就指向这个空对象。 - 接着
c模块完成剩余代码:定义C类,把C赋值给module.exports,然后c加载完成,回到b模块的执行。
- 在
- 回到
b模块,继续执行剩余代码:定义class B(并设置静态属性someStaticVar:1),然后把B赋值给b的module.exports——这一步是把b模块的导出引用从原来的空对象换成了B类,但**c模块里的B变量依然指向之前的那个空对象**,不会自动更新。 - 回到
a模块,b加载完成,a里的B变量指向的是完整的B类,所以console.log B.someStaticVar输出1。 - 然后
a加载./c,因为c已经加载过了,直接取缓存里的C类。调用C.checkB()时,C类里的checkB方法访问的是c模块里的那个B变量——也就是之前的空对象,自然没有someStaticVar属性,所以输出undefined。
修复方案
你可以调整模块的依赖结构避免循环依赖,或者延迟对依赖模块的访问:
比如修改c.coffee,不在模块顶部加载B,而是在checkB方法内部动态获取:
class C @checkB: -> B = require './b' console.log B.someStaticVar module.exports = C
这样每次调用checkB时,都会获取b模块最终导出的B类,就能正确拿到someStaticVar的值了。
内容的提问来源于stack exchange,提问作者BobJoe
相关产品推荐
相关产品推荐

