node模块循环依赖问题

代码中如果存在循环引用时会如何加载?以如下示例代码说明,代码结构如下所示:

fileA.js
fileB.js
main.js

fileA.js代码如下:

module.exports = {
    testA,
    callB
}

const fileB = require("./fileB");

function testA() {
    console.log('in fileA, this is fileA')
}

function callB() {
    console.log('--------in fileA--------')
    fileB.testB();
    console.log('--------in fileA--------')
}

fileB.js代码如下:

const fileA = require("./fileA");

module.exports = {
    testB,
    callA
}

function testB() {
    console.log('in fileB, this is fileB')
}

function callA() {
    console.log('--------in fileB--------')
    fileA.testA();
    console.log('--------in fileB--------')
}

main.js版本1代码如下:

const fileA = require("./fileA");
const fileB = require("./fileB");


fileA.testA();
fileA.callB();
fileB.testB();
fileB.callA();

main.js版本2代码如下:

const fileB = require("./fileB");
const fileA = require("./fileA");


fileA.testA();
fileA.callB();
fileB.testB();
fileB.callA();

猜猜以上两个版本中运行结果是什么?
版本1输入结果如下:

in fileA, this is fileA
--------in fileA--------
in fileB, this is fileB
--------in fileA--------
in fileB, this is fileB
--------in fileB--------
in fileA, this is fileA
--------in fileB--------

版本2输入结果如下:

in fileA, this is fileA 
--------in fileA--------
F:\my\blog\node.module\fileA.js:14
    fileB.testB();
          ^

TypeError: fileB.testB is not a function
    at Object.callB (F:\my\blog\node.module\fileA.js:14:11)   
    at Object.<anonymous> (F:\my\blog\node.module\main.js:6:7)
    at Module._compile (node:internal/modules/cjs/loader:1256:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)
    at Module.load (node:internal/modules/cjs/loader:1119:32)
    at Module._load (node:internal/modules/cjs/loader:960:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    at node:internal/main/run_main_module:23:47

为什么会出现以上两种现象呢?想找到答案就要分析下两种情况下代码的加载路线,版本1写法中代码加载路线如下:

// 首先进入main.js,执行如下语句,require方法进入fileA执行指令,此时require方法还未返回,fileA=undefined
// main.js
const fileA = require("./fileA");
// 解释器进入fileA文件,首先对module.exports赋值,执行完该语句是,外部调用require已经可以获得方法句柄。
// fileA
module.exports = {
    testA,
    callB
}
// 解析器在fileA中,在这里加载fileB,执行会走到fileB文件中。此时相当于进入方法调用,等待require()函数返回,fileB=undefined
// fileA
const fileB = require("./fileB");

// 解释器进入fileB中
// 此时会加载fileA,由于fileA还没有执行完成,因为此处存在循环引用。node为避免无限循环加载,会复制一个未加载结束的fileA并导出fileA的句柄
// fileB
const fileA = require("./fileA");//此处的require方法不会再进入fileA中加载,而是直接返回
// **上面语句执行结束后fileA被成功赋值**
// 如果fileA中module.exports放在const fileB = require("./fileB")语句之后,此处的fileA为空对象

// 解释器继续在fileB中,执行完该句后fileB加载完毕,此时解释器返回到fileA中执行
// fileB
module.exports = {
    testB,
    callA
}
// 执行到这里fileB加载完成
// 解释器进入fileA中,由于fileB以执行结束,并对module.exports赋值,因此fileB为有效句柄
// fileA
const fileB = require("./fileB")
// **上面语句执行结束后,fileB被成功赋值**
// 执行到这里fileA加载完成

// 解释器继续回到main.js中,执行语句
// main.js
const fileB = require("./fileB");
// 由于node模块加载的缓存机制,次吃fileB已在内存中,因此这里可以直接返回fileB句柄

// 到此main.js中对fileA和fileB的加载结束,fileA

语句1直接引用fileA,fileA的写法是首先是module.exports赋值,在执行完这一句之后,main.js中调用require(“./fileA”)已经可以获得导出值。
fileA继续向下执行。 同样去分析版本2时会发现fileA中加载的fileB为空对象。

注意node.js官网文档中的这一段话:

When main.js loads a.js, then a.js in turn loads b.js. At that point, b.js tries to load a.js. In order to prevent an infinite loop, an unfinished copy of the a.js exports object is returned to the b.js module. b.js then finishes loading, and its exports object is provided to the a.js module.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值