1. 程式人生 > 其它 >你真的懂 export default 嗎?

你真的懂 export default 嗎?

export default Aexport { 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.AA 是相同的,但是因為 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.jsA 的值發生變化的時候,./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 functionexport 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"

functionclass 語句會在作用域/塊中建立識別符號,而 functionclass 語句卻不會,儘管他們的函式名和類名可以被使用。

因此,下面程式碼中:

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 被當做表示式會更明顯一些。