ES6與CommonJS中的模組處理的區別
ES6和CommonJS都有自己的一套處理模組化程式碼的措施,即JS檔案之間的相互引用。
為了方便兩種方式的測試,使用nodejs的環境進行測試
CommonJS的模組處理
使用require來引入其他模組的程式碼,使用module.exports來引出
// exportDemo.js count = 1; module.exports.count = count; module.exports.Hello = function() { var name; this.setName = function(newName) { name = newName; } this.sayHello = function() { console.log("hello Mr." + name); } this.getId = function() { return count++ } }
// requireDemo.js var {Hello} = require("./exportDemo") var hello = new Hello(); hello.setName("Blank"); hello.sayHello();
前端全棧學習交流圈:866109386,面向1-3經驗年前端開發人員,幫助突破技術瓶頸,提升思維能力,群內有大量PDF可供自取,更有乾貨實戰專案視訊進群免費領取。
在終端執行 node requireDemo.js
,返回結果為'hello Mr.Blank'
匯出的Hello函式是原函式的一次拷貝,修改Hello函式的屬性值不會對其他require的地方造成影響
var { Hello, count } = require('./exportDemo') var hello = new Hello(); // 讓count自增 console.log(hello.getId()); console.log(hello.getId()); // 發現獲取的count還是原值 console.log(count) // 真正的count其實是已經改了的 var newHello = new Hello(); console.log(newHello.getId()) var { Hello: newHello, count: newCount } = require('./exportDemo') console.log(newCount, 'newCount'); // 再次require,取得的newHello和之前require的Hello指向同一個拷貝 console.log(newHello === Hello)
ES6的模組處理
nodejs中執行ES6風格的程式碼
nodejs預設是不支援ES6的模組處理方案的。
但是在8.5.0之後,ES6程式碼的檔案格式定為mjs後,可使用 node --experimental-modules xxx.mjs 執行。
// exportDemo.mjs export let a = 1;
// importDemo.mjs import {a} from './exportDemo.mjs' console.log(a)
與CommonJS模組處理的區別
CommonJS 模組輸出的是一個值的拷貝(已在上一章驗證),ES6 模組輸出的是值的引用。
// exportDemo.mjs export let counter = 1; export function incCounter() { counter ++; }
// importDemo.mjs import { counter, incCounter } from './exportDemo.mjs' incCounter(); console.log(counter)// 列印結果為2,而不是初始值的1
CommonJS模組是執行時載入,ES6模組是編譯時輸出介面
Nodejs此類的執行環境會在一個閉包中執行CommonJS模組程式碼
(function(exports, require, module, __filename, __dirname) { // Module code actually lives in here });
ES6 模組不會快取執行結果,而是動態地去被載入的模組取值,並且變數總是繫結其所在的模組。
// exportDemo.mjs export let a = 1; export const b = 2; export let obj = {}; // importDemo.mjs import { a, b } from './exportDemo.mjs' console.log(a, b) a = 1 // 報錯,TypeError: Assignment to constant variable,export出來的值是一個只讀引用 obj.x = 1// 可以改變屬性值
前端全棧學習交流圈:866109386,面向1-3經驗年前端開發人員,幫助突破技術瓶頸,提升思維能力,群內有大量PDF可供自取,更有乾貨實戰專案視訊進群免費領取。
在ES6模組中我們更多地要去考慮語法的問題 export default
有時候我們會在程式碼發現 export default obj
的用法,那麼這個default是用來幹嘛的?
default是ES6引入的與export配套使用的關鍵字,用來給匿名物件、匿名函式設定預設的名字用的
export出來的值必須要有一個命名,否則從語法層次便會報錯
讓我們看一下以下幾個會報錯的錯誤例子
export匿名物件
export { x: 1 } // 報錯,SyntaxError:Unexpected token,這是一個編譯階段的錯誤 // 正確寫法 export default { x: 1 }
export匿名函式
export function() {}// 報錯,SyntaxError: Unexpected token ( // 正確寫法 export default function() {}
迴圈引用(recycling loading)
在複雜的模組中,可能會出現模組間的 互相引用 。
commonJS的迴圈引用執行機制
// a.js exports.loaded = false; var b = require('./b.js') console.log("b in a is " + JSON.stringify(b)) exports.loaded = true; console.log("a complete")
// b.js exports.loaded = false; var a = require('./a.js') console.log("a in b is " + JSON.stringify(a)) exports.loaded = true; console.log("b complete")
// main.js var a = require('./a.js') var b = require('./b.js') console.log("a in main is" + JSON.stringify(a)) console.log("b in main is" + JSON.stringify(b))
執行指令 nodejs main.js
時序圖下的執行步驟分解圖如下所示:

ES6的迴圈引用執行機制
一個會報錯的例子
// a.mjs import { bar } from './b.mjs' console.log(bar); export let foo = 'foo from a.mjs'
// b.mjs import { foo } from './a.mjs' console.log(foo) export let bar = 'bar from b.mjs'
// main.mjs import { foo } from './a.mjs' import { bar } from './b.mjs'
node main.mjs
時序圖下的執行步驟分解圖如下所示:

前端全棧學習交流圈:866109386,面向1-3經驗年前端開發人員,幫助突破技術瓶頸,提升思維能力,群內有大量PDF可供自取,更有乾貨實戰專案視訊進群免費領取。
ES6的迴圈引用要特別注意變數是否已被宣告,若未被宣告的塊級作用域變數被其他模組引用時,會報錯。
改進方案:迴圈引用中儘量去export可以提前確定的值(例如函式),其實我們總是希望去** 引用模組執行完全後最終確定的變數 。**
// a.mjs import { bar } from './b.mjs' console.log(bar()); export function foo() { return 'foo from a.mjs' }
// b.mjs import { foo } from './a.mjs' console.log(foo()); export function bar() { return 'bar from b.mjs' }
// main.mjs import { foo } from './a.mjs' import { bar } from './b.mjs'
node main.mjs
返回結果:
foo from a.mjsbar from b.mjs
時序圖下的執行步驟分解圖如下所示:
