ES6模塊默認導(dǎo)出和變量綁定(Default Exports and Exporting bindings)

背景

JavaScript 現(xiàn)在最主流的模塊機制是 commonjs 和 ES6 module。兩者不單是語法上有所區(qū)別,在加載的時候也有所不同,譬如 commonjs 是運行時加載,ES6模塊是編譯時輸出接口的。還有一個重要的區(qū)別是 commonjs 導(dǎo)出的東西是值拷貝,而 ES6 模塊導(dǎo)出的東西是……暫時認為是引用拷貝吧。具體表現(xiàn)出來的區(qū)別看下面的例子:

commonjs模塊

// a.js
var counter = 3;
function incCounter() {
  counter++;
}
module.exports = {
  counter: counter,
  incCounter: incCounter,
};

// main.js
var mod = require('./a');

console.log(mod.counter);  // 3
mod.incCounter();
console.log(mod.counter); // 3

可以看到 counter 在模塊內(nèi)部被改變,但是使用此模塊的代碼獲取 counter 始終是 export 時候的值,不會變。

ES6模塊

// a.js
export let counter = 3;
export function incCounter() {
  counter++;
}

// main.js
import { counter, incCounter } from './a';
console.log(counter); // 3
incCounter();
console.log(counter); // 4

ES6 模塊導(dǎo)出的變量始終指向的是模塊內(nèi)部的變量,使用時可以獲得此變量的最新值。我們叫導(dǎo)出綁定:Exporting binding。

問題

如果你去看 webpack 編譯后的實現(xiàn),它會把 counter 變量轉(zhuǎn)換成 counter 的 getter,這么就可以實現(xiàn)綁定的效果。但是在看 webpack 對默認導(dǎo)出代碼的轉(zhuǎn)換時,發(fā)現(xiàn)實現(xiàn)并不使用 getter。也就是說按這種實現(xiàn),使用 export default counter 是不會產(chǎn)生 Exporting binding??纯创a:

// a.js
let counter = 3;
export function incCounter() {
  counter++;
}
export default counter;

// main.js
import counter, { incCounter } from './a';
console.log(counter); // 3
incCounter();
console.log(counter); // 3

解釋

為什么會有這種效果?其實 export default 是一種語法糖,當(dāng)模塊只有一個導(dǎo)出的時候,簡化寫代碼人的代碼量,我們把這個語法糖還原下:

// 語法糖
// myFunc.js
function myFunc() {}
export default myFunc;
// main.js
import myFunc from './myFunc';

// 非語法糖
// myFunc.js
function myFunc() {}
export { myFunc as default };
// main.js
import { default as myFunc } from './myFunc';

也就是說把 export 的東西重命名/賦值給 default,再在 import 的時候把 default 重命名為你想要的名字。問題就出在這層語法糖的轉(zhuǎn)換上,規(guī)范對于 export default x 的行為有解釋,x 的類型不同,則行為不同:

有名字的函數(shù)和類

export default function foo() {}
export default class Bar {}

相當(dāng)于

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

class Bar {}
export { Bar as default };

沒有名字的函數(shù)和類

export default function () {}
export default class {}

相當(dāng)于

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

class *default* {}
export { *default* as default };

JS會把給匿名函數(shù)或類給一個內(nèi)部的變量*default*,然后再重命名為 default 導(dǎo)出。這個內(nèi)部變量是無法通過程序獲取到的。

原始類型

export default 1;
// --- 或者 ---
let x = 4;
export default x;

相當(dāng)于

let *default* = 1
export { *default* as default };
// --- 或者 ---
let x = 4;
let *default* = x;
export { *default* as default };

當(dāng) export default x 中的 x 是沒有名字的函數(shù)或者類,又或者是原始類型,export binding 的是內(nèi)部變量*default* 并不是 x。所以改了 x 并不等于改了*default*,自然 import 的東西沒有變化。

參考文獻

  1. https://2ality.com/2015/07/es6-module-exports.html
  2. https://stackoverflow.com/questions/39276608/is-there-a-difference-between-export-default-x-and-export-x-as-default/39277065#39277065
  3. https://github.com/rollup/rollup/issues/1078
  4. https://ponyfoo.com/articles/es6-modules-in-depth#bindings-not-values
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 官方中文版原文鏈接 感謝社區(qū)中各位的大力支持,譯者再次奉上一點點福利:阿里云產(chǎn)品券,享受所有官網(wǎng)優(yōu)惠,并抽取幸運大...
    HetfieldJoe閱讀 3,720評論 2 27
  • 模塊通常是指編程語言所提供的代碼組織機制,利用此機制可將程序拆解為獨立且通用的代碼單元。所謂模塊化主要是解決代碼分...
    MapleLeafFall閱讀 1,251評論 0 0
  • Javascript模塊化編程,已經(jīng)成為一個迫切的需求。理想情況下,開發(fā)者只需要實現(xiàn)核心的業(yè)務(wù)邏輯,其他都可以加載...
    zhoulujun閱讀 3,017評論 0 14
  • 五一期間,我們爬山時,在一顆樹枝上看見了多年不見的螳螂巢,我小心翼翼地把它摘下來并帶回了家,回家以后就順手...
    秋雨素娟閱讀 330評論 0 4
  • 一點睡意都沒有了,睜開眼睛拿起手機一看才五點多,昨晚快十一點才睡的,很少這么早睡醒。有點不一樣,體驗一下生活習(xí)慣被...
    知心玲姐閱讀 334評論 2 2

友情鏈接更多精彩內(nèi)容