模塊化的不同解決方案
追根溯源,JS這門腳本語言設計伊始就是沒有模塊化的,所以早期的全局變量容易造成命名沖突。但隨着web項目越來越大,JS的代碼量也與日俱增,於是社區就自發約定了幾種模塊化的方案:requirejs遵循AMD,seajs遵循CMD,node的module遵循CommonJS規范,雖然寫法上有所不同,都是為了能夠間接實現模塊化的基礎上保持較為一致的代碼風格。
隨着ES2015的發布,官方標准定義了一種模塊化的方案,那就是import、export。可是,標准畢竟是標准,各大瀏覽器和node終端要實現標准還是有一段距離的,目前來說都2018年了主流瀏覽器都還沒實現,還得依賴轉換工具(例如babel)轉為ES5的代碼之后瀏覽器才能解析。所以這也就解釋了為什么我們的工程化代碼中nodeJS遵循的CommonJS規范和ES6的模塊化方案並存的現象。
CommonJS
1.CommonJS中使用了require導入模塊,module.exports導出模塊,而module.exports中可以引用exports導出,這兩者有什么區別呢?
//a.js
var count = 0
module.exports = count //b.js
var result = require("./a") console.log(result) //node b.js //輸出 0
//將a.js中改為
module.exports.count =count //node b.js //輸出 {count:0}
//將a.js中改為
exports.count = count //node b.js //輸出 {count:0}
//將a.js中改為
exports= count //node b.js //輸出 {}
上面的代碼可以得出結論:module.exports和exports均指向一個空對象,而require導入的模塊是module.exports導出的模塊(當改變module.exports的指向時,require導入的是count不是{count:0};改變exports指向時,得到{ }對象),
exports只是module.exports中的一個引用(當exports導出時變量,module.exports也會增加對應的變量)
2.當使用require命令加載某個模塊時,就會運行整個模塊的代碼
//a.js
var count = 0 ; console.log('123') module.exports = count //b.js
var result = require("./a") console.log(result) //node b.js //輸出 123 0
3.對於基本數據類型,屬於復制。即會被模塊緩存。同時,在另一個模塊可以對該模塊輸出的變量重新賦值。
4.對於復雜數據類型,屬於淺拷貝。由於兩個模塊引用的對象指向同一個內存空間,因此對該模塊的值做修改時會影響另一個模塊。
// a.js
let count = 0; var add = function () { count++ } var obj = { count:10, add(){ obj.count++ } } module.exports = {add,count,obj}; //b.js
var result = require("./a") result.add() console.log(result.count)// 0
result.obj.add() console.log(result.obj.count)// 11
上述案例可以看出:當導入基本數據類型時,兩個模塊沒有任何關聯(改變a中的值對於b沒有影響),而當導入復雜數據類型時,改變a中的值,b中的值也同樣被修改了;
5.當使用require命令加載同一個模塊時,不會再執行該模塊,而是取到緩存之中的值。也就是說,CommonJS模塊無論加載多少次,都只會在第一次加載時運行一次,以后再加載,就返回第一次運行的結果,除非手動清除系統緩存。
上述代碼中,b.js中只被加載了一次
ES6模塊規范
不同於CommonJS,ES6使用 export 和 import 來導出、導入模塊。export命令規定的是對外的接口,必須與模塊內部的變量建立一一對應關系。import采用的是編譯時加載,所以import導入的模塊必須放在代碼的頂部,模塊指向也是指向同一個內存對象,所以當改變內存指向的對象的值時,導入值會隨之改變(和require導入復雜類型對象相似)
// test.js
var firstName = 'Michael'; var lastName = 'Jackson'; var year = 1958; export {firstName, lastName, year}; // demo.js
import { firstName } from './test.js' console.log(firstName); // 'Michael'
import * as test from './test.js' console.log(test); //Module{} 所有內容
import { firstName as key, lastName as value } from './test.js' console.log(key + '--' + value); // Michael--Jackson
export default(導出默認值)
var firstName = 'Michael'; var lastName = 'Jackson'; var year = 1958; export default { firstName, lastName, year }; // demo.js
import test from './test.js' console.log(test); // {firstName: "Michael", lastName: "Jackson", year: 1958}
這里相當於
import { default as test } from './test.js'
總結
CommonJS模塊規范和ES6模塊規范可以說是兩種不同的概念,其作用類似,只是規范的不同導致了使用的方法以及性能的不同