我对ES6中定义的import语句有疑问。使用ES6导入功能,您可以隐式地(因为导出整个模块)或显式(使用export语句)定义已从模块导出的给定对象。我想知道使用显式导出是否有内存优势。它只会将这些模块加载到内存中,还是会将整个模块加载到内存中,只是让您访问已定义的模块?
import {foo, bar} from '/modules/my-module.js';
foo();
bar();
// vs
import module from '/modules/my-module.js';
module.foo();
module.bar();
// This can apply to require as well
const {foo, bar} = require('/modules/my-module.js');
foo();
bar();
// vs
const module = require('/modules/my-module.js');
module.foo();
module.bar();
答案 0 :(得分:1)
回答ES2015模块(import
和export
,而不是require()
):
TL; DR - 整个模块按规范加载,但实现(或捆绑器,如果您使用的话)可能能够优化何时可以可靠地确定它可以在没有违反规范。 (它是否这样做是另一个问题。)除此之外,这意味着您的代码需要避免代码中的eval
或new Function
可以触及导入的模块绑定,而不是使用动态属性访问它。 (例如,mod.foo
没问题,但mod[name]
在运行时确定name
不会。)就个人而言,我倾向于导入我需要的东西。
更详细一点:
我们说module1.js
:
import { a } from "./module2.js";
function main() {
a();
}
main();
...和module2.js
:
export function a() {
console.log("a called");
}
export function b() {
return c() * 2;
}
function c() {
return 42;
}
如果module1
是起点,则大致如下:
module1.js
的源文本被解析,其导入和导出被识别并用于填充表示其导入和导出的实时绑定的(概念)对象;这些绑定的值尚未初始化。module2
导入,module2.js
也是如此。module2
,因此评估(运行)module2.js
的源文本,初始化其导出绑定(以及它创建和填充的其他私有内容)。module1.js
也是如此。(循环依赖变得复杂,让我们把它们放在一边。)
请记住绑定是实时的,因此如果(例如)module2
更改了a
的值,则该更改将反映在module1
正在使用的绑定中。
所以在这一点上,两个模块都已经完全加载和初始化,并且那是规范离开的地方。
遵守规范,实现可以做树摇动:识别一旦建立完整树后他们可以修剪的东西,甚至是他们可以避免首先创建的东西(尽管JavaScript,静态分析只能带你到目前为止;但是避免eval
,new Function
,动态属性访问等等。因此从理论上讲,充分优化的引擎可以摆脱module2
b
和c
,因为它们不被任何东西使用(a
没有引用它们并且没有使用eval
或new Function
,而module1
仅使用a
),或者甚至可能完全避免创建它们。
一些捆绑包也会发生树木震动,试图确定模块的哪些部分没有在任何地方使用,并将这些部分从捆绑文件中删除。
现在假设您已导入"整个模块":
import * as mod2 from "./module2.js";
function main() {
mod2.a();
}
main();
我还没有弄清楚JavaScript抖动的细节,但我感觉你已经让捆绑或JavaScript引擎的生活变得更加困难,因为mod2
有一个引用b
的属性,它必须证明您永远不会使用它。
请注意,如果您真的需要前缀,则始终可以在导入时重命名:
import { a as mod2_a } from "./module2.js";
......虽然被授予了更多的详细信息。
在es-discuss上有一些关于为import
添加更多语法以简化这一点的讨论(尽管到目前为止不是很多,但我认为这方面的重点是{{3}现在,和import()
,所以也许最终你能够做一些你列出你想要的东西和一个伪对象的东西。