ES6提供了两个关键字import
导入和export
导出,语法上有些差异。!important:
import
和export
必须,始终显示在,其各自用法的,顶级范围中。也就是说不能够在if
条件中放置import
或export
; 它们必须放在全部块
和函数
以外。函数
exporting
API Members 导出API成员export
关键字要么放在声明以前
,要么做为运算符
(排序)使用要export
的特定绑定列表
。性能
export function foo() { // .. } export var awesome = 42; var bar = [1,2,3]; export { bar };
相同的表达exports
的方法:优化
function foo() { // .. } var awesome = 42; var bar = [1,2,3]; export { foo, awesome, bar };
这些都称为 命名导出( named export)
,由于您实际上导出变量/函数/等
的名称绑定
。
任何你不标记export
,会在模块的范围内保持private
。 也就是说,尽管var bar = ..
看起来像是在顶级全局范围
声明,顶级范围
其实是模块
自己; 模块中没有全局范围
。设计
注意:模块仍然能够访问window
和全部的globals
,挂起它,只是否是做为词汇顶级范围。 可是,你真的应该远离全局模块,若是可能的话。指针
您还能够在命名导出期间对模块成员进行“重命名”(也称为别名):code
function foo() { .. } export { foo as bar };
导入此模块时,只有bar
成员名称可用于导入; foo
保持隐藏在模块内。对象
模块导出不只仅是值或引用的正常赋值,由于您习惯于=
赋值运算符。 实际上,当你导出一些东西,你正在导出一个绑定(有点像一个指针)到那个东西(变量等)。排序
在模块中,若是更改已导出绑定的变量的值,即便已经导入(参见下一部分),导入的绑定也将解析为当前(更新)的值。作用域
试想一下:开发
var awesome = 42; export { awesome }; // later awesome = 100;
导入此模块时,不管是在awesome = 100
设置以前仍是以后,一旦分配已经发生,导入的绑定将解析为值100
,而不是42
。
这是由于绑定本质上是对awesome
变量自己的引用或指针,而不是其值的副本。JS引入了ES6模块绑定,这是一个大多数史无前例的概念。
虽然您能够在模块的定义中清楚地屡次使用export
,但ES6
绝对偏好模块,具备单个导出的方法,这称为default export
。用TC39
委员会的一些成员的话来讲,若是你遵循这种模式,你就会被“简单的import语法”
所奖励,而若是你不这么作,则反过来被更冗长的语法来“惩罚”。
默认导出
将特定导出的绑定设置为导入模块时的默认值。绑定的名称字面上是默认的。正如你将在后面看到的,当导入模块绑定时,你也能够重命名它们,就像你一般使用默认导出同样。
每一个模块定义只能有一个默认值。若是模块具备默认导出
,您将看到导入语法如何更简洁。
对于默认导出
语法有一个微妙的细微差异,您应该密切注意。比较这两个片断:
function foo(..) { // .. } export default foo;
and
function foo(..) { // .. } export { foo as default };
在第一个片断中,您正在导出一个绑定到此时的函数表达式值,而不是标识符foo
。 换句话说,导出export default ..
采用一个表达式。 若是之后将foo
分配给模块中的其余值,则模块导入仍会显示最初导出的函数,而不是新值。
顺便说一下,第一个片断也能够写成:
export default function foo(..) { // .. }
警告:虽然这里function foo..
在技术上,是一个函数表达式,为了模块的内部范围的目的,它被视为一个函数声明,由于foo
名称绑定在模块的顶级做用域 称为“提高”)。 出口默认类Foo
也是如此。可是,虽然你能够作到export var foo = ..,
但你目前不能作到export default var foo = ..(或let或const)
,在一个使人沮丧的不一致的状况下。 在撰写本文时,为了一致性,已经讨论了在ES6以后很快增长这种能力。
function foo(..) { // .. } export { foo as default };
在此版本的模块导出中,默认导出绑定其实是foo
标识符而不是它的值
,因此您得到前面描述的绑定行为(即,若是您之后更改foo
的值,你在import
端看到的值也将更新)。
在默认导出语法中要很是当心这个微妙的问题,特别是若是你的逻辑须要更新导出值。 若是你历来没有计划更新默认导出的值,export default ..
是很好的。 若是您计划更新值,则必须使用export {.. as default}
。 不管哪一种方式,确保评论你的代码如何来解释你的意图!
由于每一个模块只能有一个默认值,因此您可能会试图使用一个对象的默认导出,来设计您的模块,其中包含全部的API
方法,例如:
export default { foo() { .. }, bar() { .. }, .. };
这种模式,彷佛很密切地,映射了不少开发人员,已经构建了,他们的ES6以前的模块,因此,它彷佛是,一个天然的方法。 不幸的是,它存在一些缺点,并正式地不鼓励这样作。
特别是,JS引擎,不能静态分析普通对象的内容,这意味着,它不能对静态导入性能,作一些优化。 每一个成员单独和显式导出的优势是,引擎能够进行静态分析和优化。
若是你的API,已经有多个成员,彷佛这些原则 - 每一个模块一个默认导出,全部API成员名为exports - 存在冲突,不是吗? 可是您能够有一个默认导出以及其余命名导出; 它们不是相互排斥的。
所以,替换掉当前的(不建议的)模式:
export default function foo() { .. } foo.bar = function() { .. }; foo.baz = function() { .. };
you can do that
export default function foo() { .. } export function bar() { .. } export function baz() { .. }
注意:在前面的代码段中,我使用名称foo
做为默认函数的标签。 然而,名称foo
,为了导出的目的被忽略 - default
其实是导出的名称。 当您导入此默认绑定时,您能够为其指定任何所需的名称,以下一部分中所示。
function foo() { .. } function bar() { .. } function baz() { .. } export { foo as default, bar, baz, .. };
当咱们短时间覆盖import
时,混合default
和命名exports
的影响将更加清晰。但实质上它意味着最简洁的默认import
形式,将只检索foo()
函数。用户能够另外手动将bar
和baz
列为命名导入,若是他们想要的话。
你能够想象若是你有不少命名的导出绑定,你模块的用户将是多么乏味。这里有一个通配符导入表单,您能够在单个命名空间对象中导入全部模块的exports
,可是没法将通配符导入到顶级绑定。
一样,ES6模块机制被有意设计为阻止具备大量输出的模块;相对来讲,但愿这样的方法有点困难,做为一种社会工程,它鼓励简单的模块设计,由于这将有利于大/复杂的模块设计。
我建议你不要混合default export
与命名exports
,这将是是不切实际或不须要,特别是若是你有一个大的API和重构单独的模块。在这种状况下,只需使用全部named exports,并记录您的模块的用户应该使用import * as ..
(命名空间导入)方法在单个命名空间中当即带来整个API。
咱们以前提到过,但让咱们更详细地回味一下。除了导出表达式值绑定的export default ...
外,全部其余导出表单,都正在导出绑定到本地标识符。