从ES6模块导入函数表达式或函数声明有什么区别?

时间:2016-02-05 11:32:02

标签: javascript ecmascript-6 es6-modules

据我了解(see section 16.3.2.1),ES6允许函数/类导出操作数的不同语法。差异是指导出的函数是否需要在导入时解释为函数声明,在这种情况下,您可以编写:export default function () {} // (a)或函数表达式:export default (function () {}); // (b)

作为一个可能的相关旁注:我读到进口是悬而未决的,但我不确定这意味着什么。

以这个例子为例:

import foo from 'my_module'; // (c)

据我了解,上面的语句会将导出的函数保存在foo变量中。这个变量是悬挂的,或是什么,以及什么时候?

最重要的是,当foo使用my_module导出函数时以及使用(a)导出函数时,设置(b))的区别是什么?吗

1 个答案:

答案 0 :(得分:13)

你的问题有点令人费解,但我会尽力解释一切。

让我们首先确定模块的工作原理。模块具有一组导出的名称,每个名称都引用该模块中的局部变量。导出的名称不必与本地绑定的名称相同。其中一个导出的名称可以是default,其中有特殊语法(在导出和导入时),专用于模块仅导出单个内容的情况。

  

我读到进口已经悬挂,但我不确定这意味着什么:

import { foo } from 'my_module';

是的,挂起进口申报单。与varfunction(实际上类似于every other declaration)类似,标识符foo在执行模块中的任何语句之前就可以从头开始使用。事实上,绑定甚至是在声明var iables之前创建的。

区别在于它们的初始化方式:

  • var已初始化为undefined
  • 使用函数对象初始化
  • function s和function*
  • letconstclass es未被初始化
  • 导入的绑定甚至没有真正初始化,它们被创建为指向导出的名称在导入的模块中引用的局部变量的指针
  • 导入的模块import * as …)使用模块对象进行初始化(其属性也是指针)
  

foo何时设置为引用我的导出函数?

简短的回答:在其他一切之前。

答案很长:它没有真正确定。它是对您希望保留该函数的导入模块中的局部变量的引用。当局部变量不是const时可能会改变 - 但我们通常不会期望这样。并且通常它确实包含该功能,因为导入的模块在导入模块之前完全评估。因此,如果您担心var functionName = function() {} vs function functionName() {}出现问题,您可能会感到宽慰 - 但事实并非如此。

现在回到标题问题:

  

在ES6模块中导出函数表达式和函数声明有什么区别?

没什么特别的,这两方面实际上并没有多少关系:

  • export声明将导出名称链接到模块范围中的局部变量
  • 模块范围内的所有变量都像往常一样悬挂
  • 函数声明的初始化方式与带有函数表达式as usual
  • 赋值的变量声明的初始化方式不同

当然,没有充分的理由不在任何地方使用更多的声明性函数声明;这与ES6模块没有什么不同。如果有的话,甚至可能没有理由使用函数表达式,因为声明涵盖了所有内容:

/* for named exports */
export function foo() {…}

// or
function foo() {…}
export {foo as foo}

/* for default exports */
export default function foo() {…}

// or
function foo() {…}
export {foo as default}

// or
function foo() {…}
export default foo;

// or
export default function() {…}

好的,最后两个默认导出声明实际上与前两个略有不同。链接到导出名称default的本地标识符不是foo,而是*default* - 无法重新分配。这在最后一种情况下是有意义的(没有名称foo),但在倒数第二种情况下你应该注意到foo实际上只是一个本地别名,而不是导出的变量本身。我建议不要使用这种模式。

哦,在你问之前:是的,最后一次默认导出确实也是一个函数声明,而不是表达式。 匿名函数声明。这是ES6的新功能: - )

  

那么export default function () {}export default (function () {});

之间究竟有什么区别呢

它们对于各种目的几乎都是一样的。他们拥有.name属性"default"的匿名函数,这些函数由导出名称*default*指向匿名导出的特殊default绑定持有值。
它们唯一的区别是提升 - 声明将在模块的顶部实例化它的函数,只有在模块代码的执行到达语句时才会计算表达式。但是,鉴于没有变量具有可访问的名称,除了一个非常奇怪的特殊情况外,这种行为是不可观察的:一个导入自身的模块。嗯,是的。

import def from "myself";
def(); // works and logs the message
export default function() {
    console.log("I did it!");
}

import def from "myself";
def(); // throws a TypeError about `def` not being a function
export default (function() {
    console.log("I tried!");
});

无论如何,你真的不应该做这些事情。如果要在模块中使用导出的函数,请在其声明中为其命名。

  

在这种情况下,为什么要同时使用这两种语法?

发生的情况。该规范允许 ,因为它没有做出额外的例外来禁止某些无意义的事情。 无意使用。在这种情况下,规范甚至明确禁止function语句中的classexport default表达式,而是将它们视为声明。通过使用分组运算符,您发现了一个漏洞。做得好。不要滥用它。

相关问题