export default A
和export { A as default }
乍一看是一样的,但是里面有一些细微的区别比较容易留坑。本文介绍两种写法的不同之处。
import
语句导入的是引用,不是值有导出就必然有导入,我们先明确下 import
语句的工作原理。
import { A } from './module.js';
显而易见,在上面的代码中,A
和 ./module.js
中的 A 是相同的。再看这段代码:
const module = await import('./module.js'); const { A: destructuredA } = await import('./module.js');
在这段代码中,module.A
与 A
是相同的,但是因为 destructuredA
是结构赋值,因此就有一些不同了。
我们来看下 ./module.js
:
// module.js export let A = 'initial'; setTimeout(() => { A = 'changed'; }, 500);
导入 ./module.js
的代码为 ./main.js
:
// main.js import { A as importedA } from './module.js'; const module = await import('./module.js'); let { A } = await import('./module.js'); setTimeout(() => { console.log(importedA); // "changed" console.log(module.A); // "changed" console.log(A); // "initial" }, 1000);
import
语句导入的是引用,也就是说,当 ./module.js
中 A
的值发生变化的时候,./main.js
中也会跟着变化。解构赋值获得的 A
不会变化是因为解构过程中是使用的值赋值给了新变量,而不是引用。
值得注意的是,静态语句 import { A } ...
虽然看着像解构赋值,实际上与解构赋值并不相同。
小结一下:
// 以下代码获得是引用 import { A } from './module.js'; import { A as otherName } from './module.js'; import * as module from './module.js'; const module = await import('./module.js'); // 以下代码获得的是值 let { A } = await import('./module.js');
export default
我们修改下 ./module.js
:
// module.js let A = 'initial'; export { A }; export default A; setTimeout(() => { A = 'changed'; }, 500);
同时也修改 ./main.js
:
// main.js import { A, default as defaultA } from './module.js'; import anotherDefaultA from './module.js'; setTimeout(() => { console.log(A); // "changed" console.log(defaultA); // "initial" console.log(anotherDefaultA); // "initial" }, 1000);
输出结果是 "initial"
,为什么呢?
我们知道,我们可以直接 export default 'hello';
但是却不能 export { 'hello' as A }
。规范在这两种语法上有一点不同。export default
后面的将会被作为表达式对待。因此我们可以 export default 'hello';
, 甚至可以 export default 1 + 2;
。因此,在 export default A
中,A
是作为表达式语句使用的,因此使用的是 A 的值。因此,当 A
的值在 setTimeout
中被改变的时候,export default
出去的值并没有变化。
小结一下:
// 引用 import { A } from './module.js'; import { A as otherName } from './module.js'; import * as module from './module.js'; const module = await import('./module.js'); // 值 let { A } = await import('./module.js'); // 导出引用 export { A }; export { A as otherName }; // 导出值 export default A; export default 'hello!';
export { A as default }
export {}
导出的始终是一个引用,因此:
// module.js let A = 'initial'; export { A, A as default }; setTimeout(() => { A = 'changed'; }, 500);
同样,在先前的 ./main.js
中:
// main.js import { A, default as defaultA } from './module.js'; import anotherDefaultA from './module.js'; setTimeout(() => { console.log(A); // "changed" console.log(defaultA); // "changed" console.log(anotherDefaultA); // "changed" }, 1000);
小结下:
// 导入引用 import { A } from './module.js'; import { A as otherName } from './module.js'; import * as module from './module.js'; const module = await import('./module.js'); // 导入值 let { A } = await import('./module.js'); // 导出引用 export { A }; export { A as otherName }; export { A as default }; // 导出值 export default A; export default 'hello!';
export default function
虽然,前面说过 export default
后面的会被作为表达式使用。但是也有一些例外:
// module.js export default function A() {} setTimeout(() => { A = 'changed'; }, 500);
// main.js import A from './module.js'; setTimeout(() => { console.log(A); // "changed" }, 1000);
输出 "changed"
,因为 export default function
有其特殊的语法,在这个语法中,函数是作为引用传递的。
我们稍微做一下修改:
// module.js function A() {} export default A; setTimeout(() => { A = 'changed'; }, 500);
此时控制台输出 ƒ A() {}
,export
语句不再符合 export default function
语法形式,A
便使用了值传递。
不仅仅 export default function
,export default class
也是同样的表现。
为什么呢?
原因与这些语句当被用作表达式时的表现有关。
function someFunction() {} class SomeClass {} console.log(typeof someFunction); // "function" console.log(typeof SomeClass); // "function"
如果我们将他们变成表达式:
(function someFunction() {}); (class SomeClass {}); console.log(typeof someFunction); // "undefined" console.log(typeof SomeClass); // "undefined"
function
和 class
语句会在作用域/块中创建标识符,而 function
和 class
语句却不会,尽管他们的函数名和类名可以被使用。
因此,下面代码中:
export default function someFunction() {} console.log(typeof someFunction); // "function"
如果不进行特殊处理的话,输出的将会是 "undefined"
。
小结如下:
// 导入引用 import { A } from './module.js'; import { A as otherName } from './module.js'; import * as module from './module.js'; const module = await import('./module.js'); // 导入值 let { A } = await import('./module.js'); // 导出引用 export { A }; export { A as otherName }; export { A as default }; export default function A() {} // 导出值 export default A; export default 'hello!';
在早些时候,模块中的默认导出是这样的
export default = A
,这样看来,A
被当做表达式会更明显一些。