您的问题有些复杂,但我会尽力解释一切。
首先,让我们了解模块的工作原理。一个模块有一组导出名称,每个名称都引用该模块中的本地变量。导出的名称不需要与本地绑定的名称相同。其中一个导出名称可以是“default”,针对只导出单个内容的情况,有专门的语法(在导入和导出时都适用)。
I read that imports are hoisted, but I'm not really sure what that means in this context:
import { foo } from 'my_module';
是的,导入声明是被提升的。与
var
或
function
(实际上像
其他所有声明一样)类似,标识符
foo
从一开始就可用,在模块中的任何语句执行之前。事实上,该绑定甚至在已声明的
var
iables之前创建。
不同之处在于它们的初始化方式:
var
使用undefined
进行初始化
function
和function*
使用函数对象进行初始化
let
、const
和class
未初始化
- imported bindings甚至没有真正初始化,它们只是指向导入模块中所引用的本地变量的指针
- imported modules(
import * as …
)使用模块对象进行初始化(其属性也是这样的指针)
什么时候将foo
设置为引用我的导出函数?
简短的回答:在其他所有事情之前。
长的回答:这并没有确切的规定。它是指你期望保存函数的导入模块中的局部变量。当它不是const
时,局部变量可能会改变,但我们通常不会期望这种情况发生。通常情况下,它已经包含了那个函数,因为在导入它的模块被完全评估之前,已经对导入的模块进行了完全评估。所以如果你担心var functionName = function() {} vs function functionName() {}存在问题,你可能会感到宽慰——其实并没有问题。
现在回到你的标题问题:
在ES6模块中,导出函数表达式和函数声明有什么区别?
没有什么特别的,这两个方面实际上并没有太多关系:
export
声明将导出名称链接到模块范围内的局部变量
- 模块范围内的所有变量都像往常一样被提升
- 函数声明与分配一个函数表达式的变量声明初始化方式不同,像往常一样
当然,在任何情况下都没有不使用更加声明式的函数声明的好理由;这在ES6模块之前并没有什么不同。如果有的话,甚至可能会有更少的理由使用函数表达式,因为所有内容都可以通过声明来涵盖:
export function foo() {…}
function foo() {…}
export {foo as foo}
export default function foo() {…}
function foo() {…}
export {foo as default}
function foo() {…}
export default foo;
export default function() {…}
好的,最后两个默认导出声明与前两个有些不同。与导出名称
default
关联的本地标识符不是
foo
,而是
* default *
- 它不能被重新分配。这在最后一种情况下是有意义的(其中没有名称
foo
),但在倒数第二种情况下,您应该注意到
foo
实际上只是本地别名,而不是导出的变量本身。我建议不要使用这种模式。
哦,在您询问之前:是的,最后一个默认导出确实也是一个函数声明,而不是表达式。一个匿名函数声明。这是ES6的新功能:-)
那么export default function () {}
和export default (function () {});
之间的区别是什么?
它们在所有目的上基本相同。它们是匿名函数,具有“default”名称属性,由特殊的“*default*”绑定持有,该绑定指向匿名导出值所指向的导出名称“default”。
它们唯一的区别是变量提升-声明将在模块顶部实例化其函数,表达式仅在执行模块代码达到语句时才进行评估。但是,鉴于没有可访问名称的变量,除了一个非常奇怪的特殊情况之外,这种行为是不可观察的:导入自身的模块。嗯,是的。
import def from "myself";
def();
export default function() {
console.log("I did it!");
}
import def from "myself";
def();
export default (function() {
console.log("I tried!");
});
你真的不应该这样做。如果你想在模块中使用导出的函数,就在声明中给它一个名称。
“那么为什么会有这两种语法呢?”
这种情况确实会发生。规范允许这样做是因为它没有制定额外的例外来禁止某些荒谬的事情。它并不是用来这样使用的。在这种情况下,规范甚至明确禁止在“export default”语句中使用“function”和“class”表达式,并将它们视为声明。通过使用分组运算符,你找到了一个漏洞。干得好。不要滥用它。