JavaScript模塊化:CommonJS、ES6 Module

CommonJS

前言

NodeJS中使用的模塊規範。
根據CommonJS規範,一個單獨的文件就是一個模塊(module)。加載模塊使用require方法,該方法讀取一個文件並執行,最後返回文件內部的module.exports對象。
CommonJS採用同步加載模塊的方式,只有加載完才能執行後面的操做,主要適用於服務端。es6

使用規則

一、exports與require使用的模塊導入導出規則遵循的是CommonJS(AMD和CMD)規範, 通常在NodeJS(express,koa)中使用,是相對比較先出現的規範,也是目前大多數瀏覽器支持的模塊導入導出規範。typescript

module.exports 與 exports

一、一個文件即爲一個module;
二、一個module中有不少字段,例如 id path parent exports等等,能夠直接在js文件輸出module便可查看;
三、exports表示這個module要導出的數據,module.export默認指向一個空的對象內存;
四、變量exports默認指向module.exports(引用傳遞);
五、module實際上導出的數據是在module.export這個變量中;express

require

一、使用require便可導入另外一模塊中導出的數據;segmentfault

exports與require之間的聯繫

不管模塊exports的是什麼數據類型,module.exportsrequire指向的是同一塊內存地址,任何一方改變都會影響另外一方的數據(動態)瀏覽器

案例一:緩存

// a.js文件
let aNum = 1;
let aArr = [1];
setTimeout(() => {
  aNum = 11; // 並不會影響module.exports.aNum的值
  aArr.push(11); // 會影響module.exports.aArr的值
  console.log('1s a模塊改變數據aNum&aArr:');
  console.log(`aNum=${aNum},module.exports.aNum=${module.exports.aNum}`);
  console.log(`aArr=${aArr},module.exports.aArr=${module.exports.aArr}`);
}, 1000)
setTimeout(() => {
  console.log(`3s a模塊接受數據:aNum=${module.exports.aNum},aArr=${module.exports.aArr}`);
}, 3000)
// 如下寫法等價於
// module.exports.aNum = aNum aNum屬於基本類型 值拷貝
// module.exports.aArr = aArr aArr屬於對象 值引用
module.exports = {
  aNum,
  aArr
};
console.log(`a模塊已導出數據:aNum=${module.exports.aNum},aArr=${module.exports.aArr}`);

// b.js文件
const a = require("./commonjs_a");
console.log(`b模塊已接收數據:aNum=${a.aNum},aArr=${a.aArr}`);
setTimeout(() => {
  console.log(`2s b模塊接受數據:aNum=${a.aNum},aArr=${a.aArr}`);
  a.aNum = 2;
  a.aArr = [2]
  console.log(`2s b模塊改變數據:aNum=${a.aNum},aArr=${a.aArr}`);
}, 2000)


// 輸出結果
a模塊已導出數據:aNum=1,aArr=1
b模塊已接收數據:aNum=1,aArr=1
1s a模塊改變數據aNum&aArr:
aNum=11,module.exports.aNum=1
aArr=1,11,module.exports.aArr=1,11
2s b模塊接受數據:aNum=1,aArr=1,11
2s b模塊改變數據:aNum=2,aArr=2
3s a模塊接受數據:aNum=2,aArr=2

ES6 Module

前言

使用規則

一、export與import是ES組織官方退出的模塊化方案,通常在typescript和三大框架(Angular, Vue, React)中比較常見,但目前支持這套規範的客戶端瀏覽器比較少,因此一般狀況下代碼都要通過Babel轉換成目前瀏覽器能支持的,也就是exports和require那一套。框架

export 與 export default

一、不管導出數據是什麼類型的,export導出的都是變量的引用綁定;koa

二、任何未顯式導出的變量、函數或類都是模塊私有的,沒法從模塊外部訪問;模塊化

// es6_a.mjs
export let aNum = 1;
export let aArr = [1];
setTimeout(() => {
  aNum = 11;
  aArr.push(11);
  console.log('1s a模塊改變數據aNum&aArr:');
  console.log(`aNum=${aNum},`);
  console.log(`aArr=${aArr},`);
}, 1000)
console.log(`a模塊已導出數據:aNum=${aNum},aArr=${aArr}`);

// es6_b.mjs
// import時必須加{ }
import {aNum, aArr} from './es6_a.mjs';
console.log(`b模塊已接收數據:aNum=${aNum},aArr=${aArr}`);
setTimeout(() => {
  console.log(`2s b模塊接受數據:aNum=${aNum},aArr=${aArr}`);
}, 2000);

// 輸出結果
a模塊已導出數據:aNum=1,aArr=1
b模塊已接收數據:aNum=1,aArr=1
1s a模塊改變數據aNum&aArr:
aNum=11
aArr=1,11
2s b模塊接受數據:aNum=11,aArr=1,11

二、export default有點特殊,導出的數據要根據數據類型來定,與CommonJS的module.exports有點相似函數

// es6_a.mjs
let aNum = 1;
let aArr = [1];
setTimeout(() => {
  aNum = 11;
  aArr.push(11);
  console.log('1s a模塊改變數據aNum&aArr:');
  console.log(`aNum=${aNum}`);
  console.log(`aArr=${aArr}`);
}, 1000)
// 與module.exports相似
export default {
  aNum, // 數值類型 深拷貝
  aArr // 對象類型 淺拷貝
}
console.log(`a模塊已導出數據:aNum=${aNum},aArr=${aArr}`);

// es6_b.mjs
import a from './es6_a.mjs';
console.log(`b模塊已接收數據:aNum=${a.aNum},aArr=${a.aArr}`);
setTimeout(() => {
  console.log(`2s b模塊接受數據:aNum=${a.aNum},aArr=${a.aArr}`);
}, 2000);

// 輸出結果
a模塊已導出數據:aNum=1,aArr=1
b模塊已接收數據:aNum=1,aArr=1
1s a模塊改變數據aNum&aArr:
aNum=11
aArr=1,11
2s b模塊接受數據:aNum=1,aArr=1,11

三、* 在export * from 'xxx'後面加註釋會報怪異的bug,導出的數據爲undefined

import

一、導入default的不須要使用{ },導入非默認模塊則須要加{ }

二、一個文件能夠同時導出默認的模塊和非默認的模塊,以下所示:

// es6_a.mjs
export let color = 'red';
export default function(num1, num2) {
  return num1 + num2;
}

// es6_b.mjs
// 默認值必須排在非默認值以前
import sum, { color } from  './es6_a.mjs';
console.log(sum(1,2));
console.log(color);

// 輸出結果
3
red

三、可使用as在導出和導入時重命名

// es6_a.mjs
function sum(num1, num2) {
  return num1 + num2;
}
export { sum as a };

// es6_b.mjs
import { a as aa } from './es6_a.mjs';
console.log(aa(1,2));

// 輸出結果
3

二者差別

1. CommonJS 模塊輸出的是一個值的拷貝(根據數據類型分爲深拷貝或淺拷貝),ES6 模塊輸出的是值的引用(任何數據類型)
CommonJS 模塊輸出的是值的拷貝,也就是說,一旦輸出一個值,模塊內部的變化就影響不到這個值。ES6 模塊的運行機制與 CommonJS 不同。JS 引擎對腳本靜態分析的時候,遇到模塊加載命令import,就會生成一個只讀引用。等到腳本真正執行時,再根據這個只讀引用,到被加載的那個模塊裏面去取值。換句話說,ES6 的import有點像 Unix 系統的「符號鏈接」,原始值變了,import加載的值也會跟着變。所以,ES6 模塊是動態引用,而且不會緩存值,模塊裏面的變量綁定其所在的模塊。

2. CommonJS 模塊是運行時加載,ES6 模塊是編譯時輸出接口

3. ES6 Module中導入的數據爲const,不容許從新賦值;而CommonJS中導入的數據容許從新賦值

參考1

相關文章
相關標籤/搜索