`export default x` 和 `export {x as default}` 有区别吗?

Is there a difference between `export default x` and `export {x as default}`?

我了解到,对于 ES6 模块导出,导出的内容和导入的内容之间会发生绑定,因此当导出的变量发生变化时,导入的变量将显示该变化。

但是,我也了解到,在某些情况下,导入变量仅携带对导出变量的绑定。

我的具体问题是在以下两种情况下导出变量的绑定方式是否存在差异...

// Scenario #1
let a = 5;
export default a;

// Scenario #2
let a = 5;
export { a as default };

如所述:

Mozilla Docs

场景 1

它用于命名导出

// module "my-module.js"
export function cube(x) {
  return x * x * x;
}
const foo = Math.PI + Math.SQRT2;
export { cube, foo };

场景 2

它用于导出单个值或为模块提供后备值

// module "my-module.js"
export default function cube(x) {
  return x * x * x;
}

虽然没有关于性能差异的说明。

它们在一般情况下并不相同,尽管它们在函数和 classes 的情况下可以表现相同。

let a = 4;
export default a;

等同于

let a = 4;
let *default* = a;
export {*default* as default};

意思是

let a = 4;
export default a;

a = 5;

将保留 4 作为导出值,即使模块内部的 a 已更改,而 export {a as default}; 将使导出值 5.

ECMAScript 规范定义了三种不同形式的 export default,其中包含一些示例 table http://www.ecma-international.org/ecma-262/7.0/#table-42 and in the main syntax declaration for the exports: http://www.ecma-international.org/ecma-262/7.0/#sec-exports

export default HoistableDeclaration
export default ClassDeclaration
export default [lookahead ∉ { function, class }] AssignmentExpression;

with HoistableDeclaration 在这种情况下映射到函数声明和生成器声明。

如果我们查看规范,它定义了文件内变量名称到导出名称的映射,http://www.ecma-international.org/ecma-262/7.0/#sec-exports-static-semantics-exportentries

ExportDeclaration: export default HoistableDeclaration
  Let names be BoundNames of HoistableDeclaration.
  Let localName be the sole element of names.
  Return a new List containing the Record {[[ModuleRequest]]: null,
    [[ImportName]]: null, [[LocalName]]: localName, [[ExportName]]: "default"}.

ExportDeclaration: export default ClassDeclaration
  Let names be BoundNames of ClassDeclaration.
  Let localName be the sole element of names.
  Return a new List containing the Record {[[ModuleRequest]]: null,
    [[ImportName]]: null, [[LocalName]]: localName, [[ExportName]]: "default"}.

ExportDeclaration: export default AssignmentExpression;
  Let entry be the Record {[[ModuleRequest]]: null, [[ImportName]]: null,
    [[LocalName]]: "*default*", [[ExportName]]: "default"}.
  Return a new List containing entry.

  NOTE
  "*default*" is used within this specification as a synthetic name for anonymous default export values.

BoundNames这里returns函数名或者class作为值传递,所以前两种情况

export default function fn(){}
// or 
export default function* fn(){}
// or
export default class cls {}

将为变量 fncls.

导出实时绑定

你也可以

export default function(){}
// or 
export default function*(){}
// or
export default class {}

在这种情况下,这些将导出没有实时绑定的值,因为它们没有名称。

export default AssignmentExpression ; 的最后一个例子中,这就是 export default a; 的例子所满足的。你可以注意到它有 [[LocalName]]: *default* 而不是像其他的 [[LocalName]]: localName 。那是因为 export default a; 不识别 a 作为导出的名称,它处理它作为 a 的当前值作为导出值。这与 export default 4; 没有什么不同,从规范的角度来看它没有名称。

基本上

export default function fn(){}

等同于

function fn(){}
export {fn as default};

但是

let a = 4;
export default a;

不等同于:

let a = 4;
export {a as default};