今天代码遇到方法未定义has no method问题。发现是循环引用的问题。
nodejs在遇到循环require时,会把require结果得到的结果变成空对象{}。这个结果包括的是循环引用链中的每一个。
例子:
a.js console.log(‘a.js’); var b = require(’./b’); console.log(‘a+.js’); console.log(b); exports={name:‘a’}; console.log(‘a++.js’);
b.js
console.log(‘b.js’); var c = require(’./c’); console.log(‘b+.js’); console.log©; exports={name:‘b’}; console.log(‘b++.js’);
c.js
console.log(‘c.js’); var a = require(’./a’); console.log(‘c+.js’); console.log(a); exports={name:‘c’}; console.log(‘c++.js’);
然后我执行
node a.js
打印如下:
a.js b.js c.js c+.js {} c++.js b+.js {} b++.js a+.js {} a++.js
可以看到node会按引用链引用,直到发现循环引用了为止。
然后开始回溯,所有的代码都会执行到。
唯一不同的是所有循环链中require的结果都是空对象{}.
在代码编写过程中怎样可以避免这个问题的出现呢?
不太明白你的问题,试着说一下
node
中require
的常规过程就是先检查require.cache
,有就返回其exports object
,没有就load
一遍在返回exports object
且保存到require.cache
你代码这种情况,官方文档modules有提到,此时exports
其实是一个unfinished
未完成状态的exports object
,没毛病呀
咋避免。。明白原理后,自己控制require和exports的位置呗
一般require
都集中放在头部,等你require
都执行完了,开始业务代码了,exports
也就都挂好了
循环require通常都可以改成不循环的写法
同意二楼的建议。
感觉更多可能是模块设计的不够合理。其实只要把整个循环引用链中的任一引用断开就解了。
虽然Module
对象上是有loaded
可以判断是否load
完了,但还是觉得从设计上避免比较好。
@soda-wy 从设计上仅能这样来避免。。 但话说没有了 交叉引用感觉又没有了一个强大的写法了。。。
首先产生这个现象的原因是由于 require 模块的实现:
- 根据全路径检查模块是否存在:
- 如果存在返回缓存 Module 类实例的 exports 属性
- 如果不存在,新建 Module 类实例,缓存 [全路径 - 该实例]
- 缓存后再调用 tryModuleLoad 根据文件后缀名加载模块
所以循环引用,a.js 引用 b.js,b.js 中又引用了 a.js,那么 a.js 可以引用到完整的 b 模块, 但是 b.js 只能得到 a.js 中执行 require(‘b.js’) 之前的导出的内容: 如果没有任何导出内容就是空对象 {}
我觉得要解决它,你可以把 a.js 和 b.js 都使用到的部分抽出来放到一个公共的 c.js 里面,这样子 a 和 b 都去 require(‘c.js’),可以一定程度上解决这个问题吧