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:14fileB.testB();^TypeError: fileB.testB is not a functionat 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.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/60261.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

回归预测 | MATLAB实现CSO-ELM布谷鸟算法优化极限学习机多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现CSO-ELM布谷鸟算法优化极限学习机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现CSO-ELM布谷鸟算法优化极限学习机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09;效果一览基本介…

html中如何用vue语法,并使用UI组件库 ,html中引入vue+ant-design-vue或者vue+element-plus

html中如何用vue语法&#xff0c;并使用UI组件库 前言 先说一下本次应用的场景&#xff0c;本次项目中&#xff0c;需要引入github中别人写好的插件&#xff0c;插件比较大&#xff0c;没有方法直接在自己项目中&#xff0c;把别人的项目打包合并生成html&#xff08;类似于前…

php user.ini详解

0x00 前言 本篇主要是讲解分析一下user.ini相关的内容。因为这个知识点涉及到文件上传的绕过 0x01 正文 .user.ini 文件是PHP的配置文件&#xff0c;用于自定义PHP的配置选项。该文件通常位于PHP安装目录的根目录下&#xff0c;或者在特定的网站目录下。 .user.ini 文件是一…

Qt常用快捷键

运行ctrl R编译ctrl B帮助文档 F1 ,返回ESC&#xff1b;按F2快速切换到光标选中对象的源码&#xff1b;按F4在头文件(.h)和实现文件(.cpp)之间进行切换&#xff1b;按Shift  F4在ui与.h或.cpp间切换注释 ctrl /整行代码移动 ctrl shift 上下自动对齐 ctrl i同名之间的.…

Muscles|Tissue —— 介绍

BETA —— 此功能仍然在开发测试中&#xff0c;相关文档很少或没有&#xff0c;使用时需注意&#xff1b; 可使用Vellum-based Muscles & Tissue系统&#xff0c;模拟角色的肌肉、组织、及皮肤&#xff1b;可轻易导入模型和动画&#xff0c;并快速配置解算模拟&#xff1b;…

提高企业会计效率,选择Manager for Mac(企业会计软件)

作为一家企业&#xff0c;良好的财务管理是保持业务运转的关键。而选择一款适合自己企业的会计软件&#xff0c;能够帮助提高会计效率、减少错误和节约时间。在众多的选择中&#xff0c;Manager for Mac(企业会计软件)是一款值得考虑的优秀软件。 首先&#xff0c;Manager for…

Java流式编程详细介绍

文章目录 1. 流式编程介绍2. 过滤2.1 filter2.2 distinct2.3 limit2.4 sorted2.5 skip 3. 映射3.1 map3.2 flatmap 4 查找4.1 allMatch4.2 anyMatch4.3 noneMatch4.4 findFirst4.5 findAny 5. 归约6. 收集6.1 counting6.2 maxBy,minBy6.3 summingInt、summingLong、summingDoub…

深入探索PHP编程:数组、字符串与函数

深入探索PHP编程&#xff1a;数组、字符串与函数 在PHP的世界里&#xff0c;数组、字符串和函数是无处不在且极其重要的元素。它们为开发者提供了处理数据和创建可重用代码的强大工具。本文将带您深入了解PHP中的数组、字符串以及如何创建和使用函数。 数组&#xff1a;存储多…

【JavaSE】Java数组

Java数组 什么是数组 相同数组的有序集合 数组描述的是相同类型的若干个数据&#xff0c;按照一定先后次序排列组合而成 其中&#xff0c;每个数据称为一个数组元素&#xff0c;每个数组元素通过下标来访问 数组声明创建 首先必须声明数组变量&#xff0c;才能在程序中使用…

【jvm】运行时数据区

目录 一、运行时数据区一、作用二、说明三、线程共用与私有区域 一、运行时数据区 一、作用 1.内存是非常重要的系统资源&#xff0c;是硬盘和CPU 的中间仓库及桥梁&#xff0c;承载着操作系统和应用程序的实时运行。JVM内存布局规定了Java在运行过程中内存申请、分配、管理的策…

【C语言每日一题】09. 字符菱形

题目来源&#xff1a;http://noi.openjudge.cn/ch0101/09 09 字符菱形 总时间限制: 1000ms 内存限制: 65536kB 问题描述 给定一个字符&#xff0c;用它构造一个对角线长5个字符&#xff0c;倾斜放置的菱形。 输入 输入只有一行&#xff0c; 包含一个字符。 输出 该字符构…

分布式集群——搭建Hadoop环境以及相关的Hadoop介绍

系列文章目录 分布式集群——jdk配置与zookeeper环境搭建 分布式集群——搭建Hadoop环境以及相关的Hadoop介绍 文章目录 前言 一 hadoop的相关概念 1.1 Hadoop概念 补充&#xff1a;块的存储 1.2 HDFS是什么 1.3 三种节点的功能 I、NameNode节点 II、fsimage与edits…

Kubernetes(K8s 1.28.x)部署---超详细

目录 一、基础环境配置&#xff08;所有主机均要配置&#xff09; 1、配置IP地址和主机名、hosts解析 2、关闭防火墙、禁用SELinux 3、安装常用软件 4、配置时间同步 5、禁用Swap分区 6、修改linux的内核参数 7、配置ipvs功能 二、容器环境操作 1、定制软件源 2、安…

色温曲线坐标轴的选取:G/R、G/B还是R/G、B/G ?

海思色温曲线坐标 Mstar色温曲线坐标 高通色温曲线坐标 联咏色温曲线坐标 查看各家白平衡调试界面&#xff0c;比如海思、Mstart、高通等调试资料&#xff0c;白平衡模块都是以R/G B/G作为坐标系的两个坐标轴&#xff0c;也有方案是以G/R G/B作为坐标系的两个坐标轴。 以G/R G…

STM32设计的宠物投喂器(正点原子mini开发板+2.8寸屏)

一、设计需求 【1】 项目背景 在竞争日益激烈的今天,各行各业为提高竞争力,纷纷推出了各种新、奇的事物来吸引消费者。经过长时间的市场调查,发现广大市民及民营企业家大多还采用传统的人工喂养方式,这种方式不但耗费了大量的人力资源,而且由于现在的人力成本的不断增加…

Fooocus启动时modules报错的解决方法

原理&#xff1a;是由于其他程序的安装导致modules的版本不对&#xff0c;先卸载现有版本&#xff0c;再运行run.bat让其自动安装响应的modules版本。 1、cmd运行windows dos终端。 2、将Fooocus_win64_1-1-1035文件夹备份&#xff0c;rename为Fooocus_win64_1-1-1035backup文…

【Kafka】Kafka Stream简单使用

一、实时流式计算 1. 概念 一般流式计算会与批量计算相比较。在流式计算模型中&#xff0c;输入是持续的&#xff0c;可以认为在时间上是无界的&#xff0c;也就意味着&#xff0c;永远拿不到全量数据去做计算。同时&#xff0c;计算结果是持续输出的&#xff0c;也即计算结果…

向函数传递参数(传地址)

过往课程 向函数传递参数&#xff08;传值、传引用、传const引用&#xff09; 传地址 向函数传地址&#xff0c;是指将变量的地址传递给函数。 函数通过声明参数为地址变量来接收一个变量的地址。 示例如下&#xff1a; #include <iostream> using namespace std;v…

Mybatis 日志(JDK Log)

上一篇我们介绍了Mybatis中的参数&#xff0c;本篇我们使用JDK Log打印一下Mybatis运行时的日志&#xff0c;看一下Mybatis执行的过程。 这里我选取上一篇的示例进行JDK Log的集成&#xff0c;这里如果您想对上一篇进行详细了解&#xff0c;可以参考&#xff1a; Mybatis参数…

C语言实现顺序表

顺序表 1.线性表 线性表是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串… 线性表在逻辑上是线性结构&#xff0c;也就说是连续的一条直线。但是在物理结构上并不一定是…