命名导出 vs 默认导出

836 阅读2分钟

在进行导出时,可以使用默认导出和命名导出。目前有观点认为命名导出比默认导出更好用、更准确、更易于代码维护,这个观点也被越来越多的人接受,故来对比一下默认导出和命名导出的异同。

参考文章

1.导出的是值还是对象?

一般认为,import导如入的是引用而不是值,即当导入对象在模块内值发生变化后,import 导入的对象值也应当同步变化。

①默认导出
看以下例子:500ms 后修改导出对象的值。

// module.js
let thing = 'initial';

export { thing };
export default thing;

setTimeout(() => {
  thing = 'changed';
}, 500);

在另外的文件导入:

// main.js
import { thing, default as defaultThing } from './module.js';
import anotherDefaultThing from './module.js';

setTimeout(() => {
  console.log(thing); // "changed"
  console.log(defaultThing); // "initial"
  console.log(anotherDefaultThing); // "initial"
}, 1000);

默认导出的导入结果是值而不是引用。因为默认导出可以看作一种对 “default 赋值” 的特例,就像 export default = thing 这种旧语法表达的一样,本质上是一种赋值,所以拿到的是值而不是引用。

默认导出还有另一种写法 export { thing as default } 也是这样吗?

// module.js
let thing = 'initial';

export { thing, thing as default };

setTimeout(() => {
  thing = 'changed';
}, 500);
// main.js
import { thing, default as defaultThing } from './module.js';
import anotherDefaultThing from './module.js';

setTimeout(() => {
  console.log(thing); // "changed"
  console.log(defaultThing); // "changed"
  console.log(anotherDefaultThing); // "changed"
}, 1000);

可见这种写法的默认导出,导出的都是引用。

②命名导出

看以下例子:

// module.js
export let thing = 'initial';

setTimeout(() => {
  thing = 'changed';
}, 500);

在另外的文件导入:

// main.js
import { thing as importedThing } from './module.js';
const module = await import('./module.js');
let { thing } = await import('./module.js');

setTimeout(() => {
  console.log(importedThing); // "changed"
  console.log(module.thing); // "changed"
  console.log(thing); // "initial"
}, 1000);

1s 后输出发现,前两种输出结果变了,第三种没有变。也就是对命名导出来说,前两种是引用,第三种是值。

2.结论

导出是否是引用,不取决于是否是命名导出,而是取决于写法。不同的写法产生的效果不同,即使是相同含义的不同写法,效果也不同。

只要是 export default 导出的都是值而不是引用。但存在一个特例:export default function 导出的是引用。

3.总结

  • 导出与导入均为引用时,最终才是引用。
  • 导入时,除 {} = await import()外均为引用。
  • 导出时,除 export default xxx外均为引用,特例是export default function,会导出引用。

最后,尽量使用命名导出吧。