从 ES6 模块导入函数表达式或函数声明有什么区别?
What is the difference between importing a function expression or a function declaration from a ES6 module?
据我了解 (see section 16.3.2.1),ES6 允许函数/class 导出操作数使用不同的语法。区别在于导出的函数是否需要在导入时解释为函数声明,在这种情况下你写:export default function () {} // (a)
或作为函数表达式:export default (function () {}); // (b)
.
作为一个可能的相关旁注:我读到进口被吊起,但我不太确定在这种情况下这意味着什么。
以本例为例:
import foo from 'my_module'; // (c)
据我了解,上述语句会将我导出的函数保存在 foo
变量中。该变量是否已提升,或者是什么,何时提升?
最重要的是,my_module
使用 (a)
导出函数和使用 (a)
导出函数时有什么区别(在设置 foo
方面) =17=]?
你的问题有点绕,但我会尽力解释清楚。
让我们首先确定模块的一般工作方式。一个模块有一组导出名称,每个名称都引用该模块中的一个局部变量。导出的名称不需要与本地绑定的名称相同。其中一个导出的名称可以是 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
(实际上像 every other declaration)标识符 foo
从一开始就可用,在执行模块中的任何语句之前。事实上,绑定甚至在声明 var
iables.
之前创建
区别在于它们是如何初始化的:
var
s 初始化为 undefined
function
s 和 function*
s 使用函数 object 初始化
let
、const
和 class
es 未初始化
- 导入的绑定甚至没有真正初始化,它们被创建为指向导出名称在导入模块中引用的局部变量的指针
- 导入的模块 (
import * as …
) 用模块object 初始化(其属性也是这样的指针)
When is foo
set to refer to my exported function?
简短的回答:在一切之前。
长答案:还没有真正确定。它是对您希望保存函数的导入模块中的局部变量的引用。当它不是 const
时,局部变量可能会改变——但我们通常当然不希望这样。通常它确实已经包含该功能,因为 导入的模块在 导入它的模块之前被完全评估。因此,如果您担心 var functionName = function() {} vs function functionName() {} 有问题,您可能会松一口气 - 没有。
现在回到你的标题问题:
What is the difference between exporting a function expression and a function declaration in a ES6 module?
没什么特别的,这两个方面其实并没有太大的关系:
export
声明 link 模块作用域中局部变量的导出名称
- 像往常一样提升模块范围内的所有变量
- 函数声明的初始化方式不同于使用函数表达式赋值的变量声明,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() {…}
好的,最后两个默认导出声明实际上与前两个有点不同。 linked 到导出名称 default
的本地标识符不是 foo
,而是 *default*
- 它无法重新分配。这在最后一种情况下是有意义的(没有名称 foo
),但在 second-to-last 情况下你应该注意到 foo
实际上只是一个本地别名,而不是导出变量本身.我建议不要使用这种模式。
哦,在你问之前:是的,最后一个默认导出确实也是一个函数声明,而不是一个表达式。 匿名函数声明。这是 ES6 的新功能:-)
So what exactly is the difference between export default function () {}
and 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!");
});
无论如何你真的不应该做这些事情。如果您想在模块中使用导出函数,请在其声明中为其命名。
In that case, why have both syntaxes?
发生了。规范 允许 因为它不会做出额外的例外来禁止某些无意义的事情。 不打算使用。在这种情况下,规范甚至明确禁止在 export default
语句中使用 function
和 class
表达式,而是将它们视为声明。通过使用分组运算符,您发现了一个漏洞。做得好。不要滥用它。
据我了解 (see section 16.3.2.1),ES6 允许函数/class 导出操作数使用不同的语法。区别在于导出的函数是否需要在导入时解释为函数声明,在这种情况下你写:export default function () {} // (a)
或作为函数表达式:export default (function () {}); // (b)
.
作为一个可能的相关旁注:我读到进口被吊起,但我不太确定在这种情况下这意味着什么。
以本例为例:
import foo from 'my_module'; // (c)
据我了解,上述语句会将我导出的函数保存在 foo
变量中。该变量是否已提升,或者是什么,何时提升?
最重要的是,my_module
使用 (a)
导出函数和使用 (a)
导出函数时有什么区别(在设置 foo
方面) =17=]?
你的问题有点绕,但我会尽力解释清楚。
让我们首先确定模块的一般工作方式。一个模块有一组导出名称,每个名称都引用该模块中的一个局部变量。导出的名称不需要与本地绑定的名称相同。其中一个导出的名称可以是 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
(实际上像 every other declaration)标识符 foo
从一开始就可用,在执行模块中的任何语句之前。事实上,绑定甚至在声明 var
iables.
区别在于它们是如何初始化的:
var
s 初始化为undefined
function
s 和function*
s 使用函数 object 初始化
let
、const
和class
es 未初始化- 导入的绑定甚至没有真正初始化,它们被创建为指向导出名称在导入模块中引用的局部变量的指针
- 导入的模块 (
import * as …
) 用模块object 初始化(其属性也是这样的指针)
When is
foo
set to refer to my exported function?
简短的回答:在一切之前。
长答案:还没有真正确定。它是对您希望保存函数的导入模块中的局部变量的引用。当它不是 const
时,局部变量可能会改变——但我们通常当然不希望这样。通常它确实已经包含该功能,因为 导入的模块在 导入它的模块之前被完全评估。因此,如果您担心 var functionName = function() {} vs function functionName() {} 有问题,您可能会松一口气 - 没有。
现在回到你的标题问题:
What is the difference between exporting a function expression and a function declaration in a ES6 module?
没什么特别的,这两个方面其实并没有太大的关系:
export
声明 link 模块作用域中局部变量的导出名称- 像往常一样提升模块范围内的所有变量
- 函数声明的初始化方式不同于使用函数表达式赋值的变量声明,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() {…}
好的,最后两个默认导出声明实际上与前两个有点不同。 linked 到导出名称 default
的本地标识符不是 foo
,而是 *default*
- 它无法重新分配。这在最后一种情况下是有意义的(没有名称 foo
),但在 second-to-last 情况下你应该注意到 foo
实际上只是一个本地别名,而不是导出变量本身.我建议不要使用这种模式。
哦,在你问之前:是的,最后一个默认导出确实也是一个函数声明,而不是一个表达式。 匿名函数声明。这是 ES6 的新功能:-)
So what exactly is the difference between
export default function () {}
andexport 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!");
});
无论如何你真的不应该做这些事情。如果您想在模块中使用导出函数,请在其声明中为其命名。
In that case, why have both syntaxes?
发生了。规范 允许 因为它不会做出额外的例外来禁止某些无意义的事情。 不打算使用。在这种情况下,规范甚至明确禁止在 export default
语句中使用 function
和 class
表达式,而是将它们视为声明。通过使用分组运算符,您发现了一个漏洞。做得好。不要滥用它。