做者:Dmitri Pavlutin翻译:疯狂的技术宅javascript
https://dmitripavlutin.com/ja...前端
未经容许严禁转载java
让咱们从一个问题开始。程序员
名为 increment
的 ES2015 模块包含如下代码:面试
// increment.js let counter = 0; counter++; export default counter;
而后在另外一个模块 consumer
中,将上述模块 increment
导入两次:segmentfault
// consumer.js import counter1 from './increment'; import counter2 from './increment'; counter1; // => ??? counter2; // => ???
问题是:当 consumer
模块运行时,变量 counter1
和 counter2
的内容是什么?浏览器
要回答这个问题,首先你须要了解 JavaScript 如何评估和导入模块。服务器
理解 JavaScript 内部原理的一个好方法是查看其说明。微信
根据规范,每一个 JavaScript 模块都与模块记录相关联。模块记录具备方法 Evaluate()
,该方法对模块进行评估:多线程
若是该模块已经被成功评估,则返回 undefined
;……不然,即可递归地评估此模块全部的模块依赖性,而后再评估此模块。
因此同一模块仅被评估一次。
不幸的是,问题不止于此。如何确保使用相同路径两次调用 import 语句返回相同的模块?
将路径(也称为说明符)关联到具体模块的职责由 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()
的具体实现。
查看规范以后,你将知道对 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
。
仅经过研究提出的简单问题,就能够找到有关如何评估和导入 JavaScript 模块的详细信息。
规则很是简单:同一模块仅被评估一次,换句话说,模块级做用于仅被执行一次。若是评估后的模块再次导入,则会跳过第二次评估,并使用已解决的已导出文件。
若是某个模块屡次导入但使用相同的说明符(即路径),则 JavaScript 规范可确保你将获得相同的模块实例。