ES6模块如何导入自身?

16

我有一个名为fooModule的模块。在这个模块内部,我导入了fooModule(即自身):

import * as fooModule from './fooModule';

export function logFoo() {
  console.log(fooModule)
}
当调用logFoo()函数时,我可以看到fooModule的所有导出内容。这是如何实现的?

1
你想做什么? - anshuVersatile
8
导入不是必须的,导入和解析、执行是分开的。首先会解析并解决导入。在代码执行时,所有绑定已经被解决了。 - Joseph
2
最佳实践是不要这样做。 - The Reason
2
@TheReason 实际上,导入自己的模块命名空间对象可以是一个有用的事情,它有助于避免一些重复的代码。 - Bergi
1
如果您需要按名称查找事物,通常会使用带有相应值作为属性的对象字面量。如果它们恰好是您的导出内容,您可以直接使用模块命名空间对象。 - Bergi
显示剩余2条评论
1个回答

19
声明式的导入/导出可以处理循环依赖问题。在你的情况下,这个循环是最小长度的 :-)
解决方法是 import 并不会将一个值导入到变量中,而是使变量成为对导出变量的引用。请查看此处的一个可变变量示例,以及此问题获取确切术语。
对于模块命名空间对象,情况也是一样的 - 它们的属性只是解析为实际导出变量的 getter 。
因此,当你的模块被加载并评估时,会发生以下步骤:
  1. 源代码进行静态分析以构建依赖图
  2. 创建模块作用域
  3. 由于你的模块的唯一依赖项是自身,并且已经初始化,它不需要等待
  4. 创建 fooModule 变量,并将其实例化为一个对象,该对象具有模块的导出名称,即 ["logFoo"]fooModule.logFoo 属性变成了 getter ,它将求值为模块作用域中的 logFoo 变量(如果你使用了 export {A as B},那么 fooModule.B 将解析为 A,但在你的情况下两个名称相同)。
  5. 模块作用域中的变量声明创建变量,在你的情况下为 logFoo,并且函数声明被初始化(即 logFoo 被赋予函数)
  6. 运行模块代码(在你的情况下,什么都没发生)
现在当你在导入它的模块中调用 logFoo 时,fooModule 将引用包含 logFoo 的命名空间。没有魔法 :-)

我非常感激您在SO上花费的所有时间,但我无法理解这种行为的用例。您之前提到过,这可以用于避免重复代码,但是什么情况下语义上讲某个东西依赖于自身是有意义的呢? - aaaaaa
3
我只能想到两种用法:获取自己的模块命名空间对象(用于动态属性访问,用于默认导出等-避免手动构造此类对象),以及访问自己的匿名默认导出(当由于某些原因无法仅仅为其命名)。 - Bergi
我很好奇,使用webpack/vite时,这种自我循环导入是否会有性能损失? - Wenfang Du
1
@WenfangDu 我不会期望有任何区别,打包程序应该对所有类型的模块导入执行完全相同的操作,无论是否存在循环依赖。 - Bergi
感谢您的快速回复。根据我的经验,循环引用通常会导致一些问题(https://spin.atomicobject.com/2018/06/25/circular-dependencies-javascript/),因此我对此非常谨慎。 - Wenfang Du
1
@WenfangDu 我并不是说循环依赖不会引起问题,我只能支持你文章中引用的“需要仔细规划才能使循环模块依赖在应用程序中正常工作”的观点。但是,这不应该对性能产生影响,并且从自我重要性来看,也不会出现非明显的时间死区问题。 - Bergi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接