关于javascript:精读默认命名导出的区别

47次阅读

共计 3790 个字符,预计需要花费 10 分钟才能阅读完成。

从代码可维护性角度登程,命名导出比默认导出更好,因为它缩小了因援用产生重命名状况的产生。

但命名导出与默认导出的区别不止如此,在逻辑上也有很大差别,为了缩小开发时在这方面栽跟头,有必要提前理解它们的区别。

本周找来了这方面很好的的文章:export-default-thing-vs-thing-as-default,先形容梗概,再谈谈我的了解。

概述

个别咱们认为,import 导入的是援用而不是值,也就是说,当导入对象在模块内值发生变化后,import 导入的对象值也该当同步变动。

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

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

下面的例子,500ms 后批改导出对象的值。

// 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 后输入发现,前两种输入后果变了,第三种没有变。也就是对命名导出来说,前两种是援用,第三种是值。

但默认导出又不一样:

// 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);

可见,这种默认导出,导出的都是援用。所以导出是否是援用,不取决于是否是命名导出, 而是取决于写法 。不同的写法成果不同,哪怕雷同含意的不同写法,成果也不同。

难道是写法的问题吗?是的,只有是 export default 导出的都是值而不是援用。但可怜的是,存在一个特例:

// module.js
export default function thing() {}

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

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

为什么 export default function 是援用呢?起因是 export default function 是一种特例,这种写法就会导致导出的是援用而不是值。如果咱们用失常形式导出 Function,那仍然遵循后面的规定:

// module.js
function thing() {}

export default thing;

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

只有没有写成 export default function 语法,哪怕导出的对象是个 Function,援用也不会变动。所以取决成果的是写法,而与导出对象类型无关。

对于循环援用也有时而失效,时而不失效的问题,其实也取决于写法。上面的循环援用是能够失常工作的:

// main.js
import {foo} from './module.js';

foo();

export function hello() {console.log('hello');
}
// module.js
import {hello} from './main.js';

hello();

export function foo() {console.log('foo');
}

为什么呢?因为 export function 是一种特例,JS 引擎对其做了全局援用晋升,所以两个模块都能各自拜访到。上面形式就不行了,起因是不会做全局晋升:

// main.js
import {foo} from './module.js';

foo();

export const hello = () => console.log('hello');
// module.js
import {hello} from './main.js';

hello();

export const foo = () => console.log('foo');

所以是否失效取决于是否晋升,而是否晋升取决于写法。当然上面的写法也会循环援用失败,因为这种写法会被解析为导出值:

// main.js
import foo from './module.js';

foo();

function hello() {console.log('hello');
}

export default hello;

作者的摸索到这里就完结了,咱们来整顿一下思路,尝试了解其中的法则。

精读

能够这么了解:

  1. 导出与导入均为援用时,最终才是援用。
  2. 导入时,除 {} = await import() 外均为援用。
  3. 导出时,除 export default thingexport default 123 外均为援用。

对导入来说,{} = await import() 相当于从新赋值,所以具体对象的援用会失落,也就是说异步的导入会从新赋值,而 const module = await import() 援用不变的起因是 module 自身是一个对象,module.thing 的援用还是不变的,即使 module 是被从新赋值的。

对导出来说,默认导出能够了解为 export default = thing 的语法糖,所以 default 自身就是一个新的变量被赋值,所以根底类型的援用无奈被导出也很正当。甚至 export default '123' 是非法的,而 export {'123' as thing} 是非法的也证实了这一点,因为命名导出实质是赋值到 default 变量,你能够用已有变量赋值,也能够间接用一个值,但命名导出不存在赋值,所以你不能用一个字面量作命名导出。

而导出存在一个特例,export default function,这个咱们尽量少写就行了,写了也无所谓,因为函数放弃援用不变个别不会引发什么问题。

为了保障导入的总是援用,一方面尽量用命名导入,另一方面要留神命名导出。如果这两点都做不到,能够尽量把须要维持援用的变量应用 Object 封装,而不要应用简略变量。

最初对循环依赖而言,只有 export default function 存在申明晋升的 Magic,能够保障循环依赖失常 Work,但其余状况都不反对。要防止这种问题,最好的方法是不要写出循环依赖,遇到循环依赖时应用第三个模块作中间人。

总结

个别咱们都心愿 import 到的是援用而不是瞬时值,但因为语义与非凡语法糖的起因,导致并不是所有写法成果都是统一的。

我也认为不须要背下来这些导入导出细枝末节的差别,只有写模块时都用标准的命名导入导出,少用默认导出,就能够在语义与理论体现上躲避掉这些问题啦。

探讨地址是:精读《export 默认 / 命名导出的区别》· Issue #342 · dt-fe/weekly

如果你想参加探讨,请 点击这里,每周都有新的主题,周末或周一公布。前端精读 – 帮你筛选靠谱的内容。

关注 前端精读微信公众号

<img width=200 src=”https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg”>

版权申明:自在转载 - 非商用 - 非衍生 - 放弃署名(创意共享 3.0 许可证)

正文完
 0