分享

当一个模块被引入两次时,会发生什么?

 西北望msm66g9f 2020-05-06

让我们从一个问题开始。

名为 increment 的 ES2015 模块包含以下代码:

1// increment.js
2let counter = 0;
3counter++;
4
5export default counter;

然后在另一个模块 consumer 中,将上述模块 increment 导入两次:

1// consumer.js
2import counter1 from './increment';
3import counter2 from './increment';
4
5counter1; // => ???
6counter2; // => ???

问题是:当 consumer 模块运行时,变量 counter1counter2 的内容是什么?

要回答这个问题,首先你需要了解 JavaScript 如何评估和导入模块。

1. 模块评估

理解 JavaScript 内部原理的一个好方法是查看其说明。

根据规范,每个 JavaScript 模块都与模块记录相关联。模块记录具有方法 Evaluate(),该方法对模块进行评估:


如果该模块已经被成功评估,则返回 undefined;……否则,便可递归地评估此模块所有的模块依赖性,然后再评估此模块。


所以同一模块仅被评估一次。

不幸的是,问题不止于此。如何确保使用相同路径两次调用 import 语句返回相同的模块?

2. 解决导入问题

将路径(也称为说明符)关联到具体模块的职责由 HostResolveImportedModule() 执行操作。

1import module from 'path';

根据规则:


HostResolveImportedModule 的实现必须符合以下要求:

  • 正常的返回值必须是  Module Record 的具体子类的实例。

  • 如果与 referencingScriptOrModule, specifier对相对应的 Module Record 不存在或无法创建,则必须引发异常。

  • 每次使用特定的 referencingScriptOrModule, specifier 对作为参数调用此操作时,如果正常完成,则必须返回相同的 Module Record 实例。


让我们以一种易于理解的方式看看都发生了什么。

HostResolveImportedModule(referencingScriptOrModule, specifier) 是一个抽象操作,该操作返回对应于ReferencingScriptOrModule, specifier 的模块:

  • 参数 referencingScriptOrModule 是当前模块,即进行导入的模块。

  • 参数 specifier 是对应 import 语句中模块路径的字符串。

最后,HostResolveImportedModule() 从相同路径导入模块时,将导入相同模块:

1import moduleA from 'path';
2import moduleB from 'path';
3import moduleC from 'path';
4
5// moduleA, moduleB and moduleC are equal
6
7moduleA === moduleB; // => true
8moduleB === moduleC; // => true

有趣的是,规范指出主机(指浏览器,Node 或任何尝试运行 JavaScript 的东西)必须提供 HostResolveImportedModule() 的具体实现。

3. 答案

查看规范之后,你将知道对 JavaScript 模块进行了一次评估。另外,从相同路径导入模块时,将返回相同的模块实例。

让我们回到问题。

increment 模块总是被评估一次:

1// increment.js
2let counter = 0;
3counter++;
4
5export default counter;

不管 increment 模块被导入多少次,counter++ 语句仅执行一次。默认导出的 counter 变量的值为 1

现在看 consumer 模块:

1// consumer.js
2import counter1 from './increment';
3import counter2 from './increment';
4
5counter1; // => 1
6counter2; // => 1

import counter1 from './increment'import counter2 from './increment'  有相同的路径:'./increment'。所以你将会导入相同的模块实例。

最后,consumer 内部的 counter1counter2 变量都等于 1

4. 结论

仅通过研究提出的简单问题,就可以找到有关如何评估和导入  JavaScript 模块的详细信息。

规则非常简单:同一模块仅被评估一次,换句话说,模块级作用于仅被执行一次。如果评估后的模块再次导入,则会跳过第二次评估,并使用已解决的已导出文件。

如果某个模块多次导入但使用相同的说明符(即路径),则 JavaScript 规范可确保你将得到相同的模块实例。

原文链接



https:///javascript-module-import-twice/


 

❤️ 看完三件事

如果你觉得这篇内容对你挺有启发,我想邀请你帮我三个小忙:

  1. 点个「在看」,让更多的人也能看到这篇内容(喜欢不点在看,都是耍流氓 -_-)

  2. 关注我的博客 https://github.com/SHERlocked93/blog,让我们成为长期关系

  3. 关注公众号「前端下午茶」,持续为你推送精选好文,也可以加我为好友,随时聊骚。


在看点这里

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多