【问题标题】:What is the difference between importing a function expression or a function declaration from a ES6 module?从 ES6 模块导入函数表达式或函数声明有什么区别?
【发布时间】:2016-05-15 08:51:14
【问题描述】:

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

作为一个可能的相关旁注:我读到进口被提升,但我不确定这在这种情况下意味着什么。

以本例为例:

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

据我了解,上述语句会将我导出的函数保存在 foo 变量中。该变量是否被提升,或者是什么,何时?

最重要的是,my_module 使用(a) 导出函数和使用(b) 导出函数时有什么区别(在设置foo 方面)?

【问题讨论】:

  • 您的问题是关于导出还是导入?
  • 关于导入

标签: javascript ecmascript-6 es6-modules


【解决方案1】:

你的问题有点复杂,但我会尽力解释一切。

让我们首先确定模块的一般工作方式。一个模块有一组导出的名称,每个名称都引用该模块中的一个局部变量。导出的名称不需要与本地绑定的名称相同。其中一个导出的名称可以是default,对于模块仅导出单个事物的情况,有专门的特殊语法(在导出和导入中)。

我读到进口被提升了,但我不确定这在这种情况下意味着什么:

import { foo } from 'my_module';

是的,进口申报已被吊起。与varfunction(实际上就像every other declaration)类似,标识符foo 从一开始就可用,在模块中的任何语句执行之前。事实上,绑定甚至是在声明的variables 之前创建的。

不同之处在于它们是如何初始化的:

  • vars 初始化为 undefined
  • functions 和 function*s 用函数对象初始化
  • letconstclasses 未初始化
  • 导入的绑定甚至没有真正初始化,它们被创建为一个指向导出名称在导入模块中引用的局部变量的指针
  • 导入的模块 (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!");
});

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

既然如此,为什么有两种语法?

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

【讨论】:

  • 您对吊装的解释正是我想要的,谢谢。不过,我似乎还没有很好地说明我的中心问题。我编辑了主要帖子以使其更清晰。
  • @c10b10:好的。我稍后会编辑。现在:差异是绝对简约的,但您绝对应该使用声明。
  • 只要你有时间,我很想看看为什么。作为一个相关的旁注,在非模块上下文中,函数表达式对内存的负担不是更少吗?
  • @c10b10:函数对象就是函数对象。它是如何创建的(表达式、声明、new Function())对记忆无关紧要。
  • 在这种情况下,为什么要如何导出它呢?为什么有两种语法?
猜你喜欢
  • 2010-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-20
  • 2011-06-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多