commonjs, es6 module什么是循环依赖?

2023-10-30

什么是循环依赖?

循环依赖是指模块A依赖于模块B,同时模块B依赖于模块A。比如下面这个例子:

// foo.js
const bar = require('./bar.js');
console.log('value of bar:', bar);
module.exports = 'This is foo.js';

// bar.js
const foo = require('./foo.js');
console.log('value of foo:', foo);
module.exports = 'This is bar.js';

// index.js
require('./foo.js');

在这里,index.js是执行入口,它加载了foo.jsfoo.jsbar.js之间存在循环依赖
理想状态下我们希望控制台上输出:

value of foo: This is foo.js
value of bar: This is bar.js

实际输出却是:

value of foo: {}
value of bar: This is bar.js

为什么foo的值会是一个空对象呢?让我们从头梳理一下代码的实际执行顺序。

  1. index.js导入了foo.js,此时开始执行foo.js中的代码。
  2. foo.js的第1句导入了bar.js,这时foo.js不会继续向下执行,而是进入了bar.js内部。
  3. 在bar.js中又对foo.js进行了require,这里产生了循环依赖。需要注意的是,执行权并不会再交回foo.js,而是直接取其导出值,也就是module.exports。但由于foo.js未执行完毕,导出值在这时为默认的空对象,因此当bar.js执行到打印语句时,我们看到控制台中的value of foo就是一个空对象。
  4. bar.js执行完毕,将执行权交回foo.js。
  5. foo.js从require语句继续向下执行,在控制台打印出value of bar(这个值是正确的),整个流程结束。

由上面可以看出,尽管循环依赖的模块均被执行了,但模块导入的值并不是我们想要的。因此在CommonJS中,若遇到循环依赖我们没有办法得到预想中的结果。

我们再从Webpack的实现角度来看,将上面例子打包后,bundle中有这样一段代码非常重要:

// The require function
function __webpack_require__(moduleId) {
  if(installedModules[moduleId]) {
    return installedModules[moduleId].exports;
  }
  // Create a new module (and put it into the cache)
  var module = installedModules[moduleId] = {
    i: moduleId,
    l: false,
    exports: {}
  };
  ...
}

当index.js引用了foo.js之后,相当于执行了这个__webpack_require__函数,初始化了一个module对象并放入installedModules中。当bar.js再次引用foo.js时,又执行了该函数,但这次是直接从installedModules里面取值,此时它的module.exports是一个空对象。这就解释了上面在第3步看到的现象。

接下来让我们使用ES6 Module的方式重写上面的例子。

// foo.js
import bar from './bar.js';
console.log('value of bar:', bar);
export default 'This is foo.js';

// bar.js
import foo from './foo.js';
console.log('value of foo:', foo);
export default 'This is bar.js';

// index.js
import foo from './foo.js';

执行结果如下:

value of foo: undefined
foo.js:3 value of bar: This is bar.js

很遗憾,在bar.js中同样无法得到foo.js正确的导出值,只不过和CommonJS默认导出一个空对象不同,这里获取到的是undefined。

那怎么才能规避循环依赖呢!

避免循环依赖的方法

//index.js
import foo from './foo.js';
foo('index.js');

// foo.js
import bar from './bar.js';
function foo(invoker) {
    console.log(invoker + ' invokes foo.js');
    bar('foo.js');
}
export default foo;

// bar.js
import foo from './foo.js';
let invoked = false;
function bar(invoker) {
    if(!invoked) {
        invoked = true;
        console.log(invoker + ' invokes bar.js');
        foo('bar.js');
    }
}
export default bar;

上面代码的执行结果如下:

index.js invokes foo.js
foo.js invokes bar.js
bar.js invokes foo.js

ES6 Module的特性使其可以更好地支持循环依赖,只是需要由开发者来保证当导入的值被使用时已经设置好正确的导出值。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

commonjs, es6 module什么是循环依赖? 的相关文章

随机推荐

  • MYSQL查询当前表存在哪些索引

    查看表存在的索引 show index from table name 表名 结果列表中各字段的含义 Non unique 如果索引不能包括重复词 则为0 如果可以 则为1 Key name 索引名称 Seq in index 索引中的列序
  • TIKTOK视频:视频内容打造需要注意的几点 抓住流量密码

    TIKTOK视频 视频内容打造需要注意的几点 抓住流量密码 大家好 我是项柚 一个专注于讨论TikTok玩法的跨境电商自媒体人 每天不断输出干货给需要的朋友 大家都知道 欧美跨境市场已经被认为是 红海 很多人已经凭着一股冲劲凭着一边做一边学
  • mybatis plus 常用方法

    学习链接 简介 MyBatis Plus 一 分页 创建分页实体 Page
  • 文盘Rust -- 给程序加个日志

    日志是应用程序的重要组成部分 无论是服务端程序还是客户端程序都需要日志做为错误输出或者业务记录 在这篇文章中 我们结合log4rs聊聊rust 程序中如何使用日志 log4rs类似java生态中的log4j 使用方式也很相似 log4rs中
  • 基于SoC FPAG实现手写体识别(HLS编译的全连接算子)

    基于SoC FPAG实现手写体识别 HLS编译的全连接算子 点击操作手册下载 完整代码 1 HLS的代码 2 SoC EDS 中 eclipse 测试代码 由于流程过多 这里采用pdf文件下载的方式 点击操作手册下载 链接 https pa
  • 北京大学肖臻老师《区块链技术与应用》公开课笔记-BTC

    本笔记为学习期间对主要知识和逻辑的记录 根据课程内容分为BTC和ETH两篇 本篇为BTC部分 北京大学肖臻老师 区块链技术与应用 公开课笔记 ETH 文章目录 01 课程简介 02 BTC 密码学原理 03 BTC 数据结构 04 BTC
  • javascript ES5中 foreach()遍历方法

    forcach array forEach function currentValue index arr currentValue 数组当前项的值 index 数组当前项的索引 可选 arr 数组对象本身 filter 方法创建一个新的数
  • Unable to launch the IIS Express Web server 问题之解决 - [Visual Studio 2015]

    背景 Visual Studio 2015 在 Debug 模式下调试失败 报错如下图所示 解决办法 删除解决方案下 vs config 文件夹内的这个配置文件 再关闭并重新运行解决方案即可进行调试
  • 清除SVN版本信息

    echo on color 2f mode con cols 80 lines 25 REM echo Deleting all svn please wait rem Delete svn in current and sub direc
  • LeetCode之Count Binary Substrings(Kotlin)

    问题 Give a string s count the number of non empty contiguous substrings that have the same number of 0 s and 1 s and all
  • 如何搭建C语言环境

    以下文章来源于 公 众 号开源电子网 读取更多技术文章 请扫码关注 如何搭建C语言环境 前言 C语言作为嵌入式开发的必备掌握技能 嵌入式能力的提升速度很大程度在于C语言的掌握能力 正所谓 工欲善其事 必先利其器 学习C语言 第一件动手的事情
  • 【餐厅点餐平台|一】项目描述+需求分析

    餐厅点餐平台导航 餐厅点餐平台 一 项目描述 需求分析 https blog csdn net weixin 46291251 article details 126414430 餐厅点餐平台 二 总体设计 https blog csdn
  • 大数据系列——Redis部署及应用

    Redis有四种部署方式 分别为单机模式 主备模式 哨兵模式 集群模式 其中单机模式比较简单 容量 处理能力有限 没有高可用 主备模式和哨兵模式本质和单机模式一样 只是主备模式保证数据高可用 哨兵模式保证数据和服务的高可用 集群模式是将数据
  • 为什么宏定义和函数定义运行结果不一样?

    函数定义 include
  • JUC-16. CAS

    想了解更多JUC的知识 JUC并发编程合集 1 CAS的概述 CAS的全称为Compare And Swap 比较并交换 它是一条CPU并发原语 比较工作内存值 预期值 和主物理内存的共享值是否相同 相同则执行规定操作 否则继续比较直到主内
  • Python中FIR滤波和小波包滤波对比(MNE脑电数据处理)

    小波变换有信号显微镜之称 在EEG分析中也有广泛的应用 印象中小波算法是来源于地球物理解释的 之前有介绍过小波的一些资料和实现 https blog csdn net zhoudapeng01 article details 1070259
  • srand((unsigned int)time(NULL))的理解(C语言)

    在c语言中 碰到这句函数 srand unsigned int time NULL 的理解 目录 1 srand与rand的关系 2 time函数的用法 3 取任意数 1 srand与rand的关系 在C中srand函数经常跟rand函数一
  • 经典/最新计算机视觉论文及代码推荐

    今日推荐几篇最新 经典计算机视觉方向的论文 涉及诸多方面 都是CVPR2022录用的文章 具体内容详见论文原文和代码链接 Convnet新活力 论文题目 A ConvNet for the 2020s 论文链接 https arxiv or
  • python中debug断点调试

    python Debug 断点调试 断点 break point 是指在代码中指定位置 当程序运行到此位置时变中断下来 并让开发者可查看此时各变量的值 因断点中断的程序并没有结束 可以选择继续执行 方法1 脚本中运行 在需要设置断点的地方
  • commonjs, es6 module什么是循环依赖?

    什么是循环依赖 循环依赖是指模块A依赖于模块B 同时模块B依赖于模块A 比如下面这个例子 foo js const bar require bar js console log value of bar bar module exports