作者:Dmitri Pavlutin翻译:疯狂的技术宅
https://dmitripavlutin.com/ja...
未经允许严禁转载
让我们从一个问题开始。
名为 increment
的 ES2015 模块包含以下代码:
// increment.js
let counter = 0;
counter++;
export default counter;
然后在另一个模块 consumer
中,将上述模块 increment
导入两次:
// consumer.js
import counter1 from './increment';
import counter2 from './increment';
counter1; // => ???
counter2; // => ???
问题是:当 consumer
模块运行时,变量 counter1
和 counter2
的内容是什么?
要回答这个问题,首先你需要了解 JavaScript 如何评估和导入模块。
1. 模块评估
理解 JavaScript 内部原理的一个好方法是查看其说明。
根据规范,每个 JavaScript 模块都与模块记录相关联。模块记录具有方法 Evaluate()
,该方法对模块进行评估:
如果该模块已经被成功评估,则返回 undefined
;……否则,便可递归地评估此模块所有的模块依赖性,然后再评估此模块。
所以同一模块仅被评估一次。
不幸的是,问题不止于此。如何确保使用相同路径两次调用 import 语句返回相同的模块?
2. 解决导入问题
将路径(也称为说明符)关联到具体模块的职责由 HostResolveImportedModule() 执行操作。
import module from 'path';
根据规则:
HostResolveImportedModule
的实现必须符合以下要求:
- 正常的返回值必须是
Module Record
的具体子类的实例。 - 如果与
referencingScriptOrModule, specifier
对相对应的Module Record
不存在或无法创建,则必须引发异常。 - 每次使用特定的
referencingScriptOrModule, specifier
对作为参数调用此操作时,如果正常完成,则必须返回相同的Module Record
实例。
让我们以一种易于理解的方式看看都发生了什么。
HostResolveImportedModule(referencingScriptOrModule, specifier)
是一个抽象操作,该操作返回对应于ReferencingScriptOrModule, specifier
的模块:
- 参数
referencingScriptOrModule
是当前模块,即进行导入的模块。 - 参数
specifier
是对应import
语句中模块路径的字符串。
最后,HostResolveImportedModule()
从相同路径导入模块时,将导入相同模块:
import moduleA from 'path';
import moduleB from 'path';
import moduleC from 'path';
// moduleA, moduleB and moduleC are equal
moduleA === moduleB; // => true
moduleB === moduleC; // => true
有趣的是,规范指出主机(指浏览器,Node 或任何尝试运行 JavaScript 的东西)必须提供 HostResolveImportedModule()
的具体实现。
3. 答案
查看规范之后,你将知道对 JavaScript 模块进行了一次评估。另外,从相同路径导入模块时,将返回相同的模块实例。
让我们回到问题。
increment
模块总是被评估一次:
// increment.js
let counter = 0;
counter++;
export default counter;
不管 increment
模块被导入多少次,counter++
语句仅执行一次。默认导出的 counter
变量的值为 1
。
现在看 consumer
模块:
// consumer.js
import counter1 from './increment';
import counter2 from './increment';
counter1; // => 1
counter2; // => 1
import counter1 from './increment'
和 import counter2 from './increment'
有相同的路径:'./increment'
。所以你将会导入相同的模块实例。
最后,consumer
内部的 counter1
和 counter2
变量都等于 1
。
4. 结论
仅通过研究提出的简单问题,就可以找到有关如何评估和导入 JavaScript 模块的详细信息。
规则非常简单:同一模块仅被评估一次,换句话说,模块级作用于仅被执行一次。如果评估后的模块再次导入,则会跳过第二次评估,并使用已解决的已导出文件。
如果某个模块多次导入但使用相同的说明符(即路径),则 JavaScript 规范可确保你将得到相同的模块实例。
本文首发微信公众号:前端先锋
欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章
欢迎继续阅读本专栏其它高赞文章:
- 深入理解Shadow DOM v1
- 一步步教你用 WebVR 实现虚拟现实游戏
- 13个帮你提高开发效率的现代CSS框架
- 快速上手BootstrapVue
- JavaScript引擎是如何工作的?从调用栈到Promise你需要知道的一切
- WebSocket实战:在 Node 和 React 之间进行实时通信
- 关于 Git 的 20 个面试题
- 深入解析 Node.js 的 console.log
- Node.js 究竟是什么?
- 30分钟用Node.js构建一个API服务器
- Javascript的对象拷贝
- 程序员30岁前月薪达不到30K,该何去何从
- 14个最好的 JavaScript 数据可视化库
- 8 个给前端的顶级 VS Code 扩展插件
- Node.js 多线程完全指南
- 把HTML转成PDF的4个方案及实现
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。