从 ES6 模块导入函数表达式或函数声明有什么区别? [英] What is the difference between importing a function expression or a function declaration from a ES6 module?

查看:22
本文介绍了从 ES6 模块导入函数表达式或函数声明有什么区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

据我所知(见第 16.3.2.1 节),ES6 允许不同的函数语法/类导出操作数.区别在于导出的函数是否需要在导入时被解释为函数声明,在这种情况下你写: export default function () {}//(a) 或作为函数表达式:export default (function(){});//(b).

As I understand it (see section 16.3.2.1), ES6 allows different syntaxes for function / class export operands. The difference refers to whether the exported function needs to be interpreted at import as a function declaration, in which case you write: export default function () {} // (a) or as a function expression: export default (function () {}); // (b).

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

As a possible related sidenote: I read that imports are hoisted, but I'm not really sure what that means in this context.

以本例为例:

从 'my_module' 导入 foo;//(C)

据我所知,上面的语句将我导出的函数保存在一个 foo 变量中.该变量是否已提升,或者是什么,何时提升?

As I understand it, the above statement will save my exported function in a foo variable. Is that variable hoisted, or what is, and when?

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

Most importantly, what is the difference (in terms of setting foo) when my_module exports the function using (a) and when it exports it using (b)?

推荐答案

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

Your question is a bit convoluted but I'll try my best to explain everything.

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

Let's first establish how modules work in general. A module has a set of exported names, each of which refer to a local variable in that module. The name of the export does not need to be the same as that of the local binding. One of the exported names can be default, for which there is special syntax (both in exporting and importing) dedicated for the case that a module only exports a single thing.

我读到导入已被提升,但我不确定在这种情况下这意味着什么:

I read that imports are hoisted, but I'm not really sure what that means in this context:

import { foo } from 'my_module';

是的,导入声明被提升.类似于 varfunction(实际上就像 其他所有声明) 在模块中的任何语句被执行之前,标识符 foo 从一开始就可用.事实上,绑定甚至在声明的 variables 之前创建.

Yes, import declarations are hoisted. Similarly to a var or function (and actually like every other declaration) the identifier foo is available right from the beginning, before any statements in the module are executed. In fact the binding is even created before those of declared variables.

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

The difference is how they are initialised:

  • vars 被初始化为 undefined
  • functions 和 function*s 用函数对象初始化
  • letconstclasses 未初始化
  • 导入的绑定甚至没有真正初始化,它们被创建为指向导出名称在导入模块中引用的局部变量的指针
  • 导入的模块(import * as ...)使用模块对象(其属性也是指针)进行初始化
  • vars are initialised with undefined
  • functions and function*s are initialised with the function object
  • let, const and classes are left uninitialised
  • imported bindings are not even really initialised, they are created as a pointer to the local variable that the exported name refers to in the imported module
  • imported modules (import * as …) are initialised with a module object (whose properties are such pointers as well)

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

When is foo set to refer to my exported function?

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

The short answer: before everything else.

答案很长:还没有真正确定.它是对您希望保存函数的导入模块中的局部变量的引用.当局部变量不是 const 时,它可能会改变——但我们通常不希望这样.并且通常它确实已经包含该函数,因为导入的模块在导入它的模块之前被完全评估.因此,如果您担心 var functionName = function() {} vs function functionName() {} 有问题,您可能会松了一口气 - 没有.

The long answer: it's not really set. It's a reference to the local variable in the imported module that you expect to hold the function. The local variable might change when it's not const - but we usually don't expect that of course. And normally it does contain that function already, because the imported module is completely evaluated before the module(s) that import it are. So if you fear there's a problem with var functionName = function() {} vs function functionName() {} you may be relieved - there is not.

现在回到你的标题问题:

Now back to your title question:

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

What is the difference between exporting a function expression and a function declaration in a ES6 module?

没什么特别的,这两个方面其实没有太大关系:

Nothing special, the two aspects actually don't have much to do with each other:

  • export 声明将导出名称链接到模块范围内的局部变量
  • 像往常一样提升模块范围内的所有变量
  • 函数声明的初始化方式与使用函数表达式赋值的变量声明不同,照常
  • export declarations link an export name to a local variable in the module scope
  • All variables in the module scope are hoisted, as usual
  • function declarations are initialised differently than variable declarations with an assignment of a function expression, as usual

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

Of course, there still are no good reasons not to use the more declarative function declarations everywhere; this is not different in ES6 modules than before. If at all, there might even be less reasons to use function expressions, as everything is covered by declarations:

/* 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 实际上只是一个本地别名,而不是导出的变量本身.我建议不要使用这种模式.

Ok, the last two default export declarations are actually a bit different than the first two. The local identifier that is linked to the exported name default is not foo, but *default* - it cannot be reassigned. This makes sense in the last case (where there is no name foo), but in the second-to-last case you should notice that foo is really just a local alias, not the exported variable itself. I would recommend against using this pattern.

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

Oh, and before you ask: Yes, that last default export really is a function declaration as well, not an expression. An anonymous function declaration. That's new with ES6 :-)

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

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

They are pretty much the same for every purpose. They're anonymous functions, with a .name property "default", that are held by that special *default* binding to to which the exported name default points to for anonymous export values.
Their only difference is hoisting - the declaration will get its function instantiated at the top of the module, the expression will only be evaluated once the execution of module code reaches the statement. However, given that there is no variable with an accessible name for them, this behavior is not observable except for one very odd special case: a module that imports itself. Um, yeah.

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!");
});

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

You really shouldn't do either of these things anyway. If you want to use an exported function in your module, give it a name in its declaration.

既然如此,为什么要同时使用这两种语法?

In that case, why have both syntaxes?

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

Happens. It's allowed by the spec because it doesn't make extra exceptions to prohibit certain nonsensical things. It is not intended to be used. In this case the spec even explicitly disallows function and class expressions in export default statements and treats them as declarations instead. By using the grouping operator, you found a loophole. Well done. Don't abuse it.

这篇关于从 ES6 模块导入函数表达式或函数声明有什么区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆