CNode

怎样可以有效的解决因为循环引用导致空对象的问题?

问答
Kkenshinhu发布于9 年前最后回复9 年前6 回复6075 浏览0 收藏

今天代码遇到方法未定义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(c); 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的结果都是空对象{}.

在代码编写过程中怎样可以避免这个问题的出现呢?

查看回复

回复 (6)

S
sodawy#1·9 年前

不太明白你的问题,试着说一下

noderequire的常规过程就是先检查require.cache,有就返回其exports object,没有就load一遍在返回exports object且保存到require.cache

你代码这种情况,官方文档modules有提到,此时exports其实是一个unfinished未完成状态的exports object,没毛病呀

咋避免。。明白原理后,自己控制require和exports的位置呗

一般require都集中放在头部,等你require都执行完了,开始业务代码了,exports也就都挂好了

D
dislido#2·9 年前

循环require通常都可以改成不循环的写法

S
sodawy#4·9 年前
引用 kenshinhu@soda wy 之后的解决方法是加个 setTimeOut 来延时加载部分的文件 但这个解决不太优雅

@kenshinhu

同意二楼的建议。

感觉更多可能是模块设计的不够合理。其实只要把整个循环引用链中的任一引用断开就解了。

虽然Module对象上是有loaded可以判断是否load完了,但还是觉得从设计上避免比较好。

H
hyj1991#6·9 年前

首先产生这个现象的原因是由于 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'),可以一定程度上解决这个问题吧

参与回复
登录后即可参与回复。登录