ES6和CommonJS都有本身的一套處理模塊化代碼的措施,即JS文件之間的相互引用。node
爲了方便兩種方式的測試,使用nodejs的環境進行測試緩存
使用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();
複製代碼
在終端執行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)
複製代碼
nodejs默認是不支持ES6的模塊處理方案的。測試
可是在8.5.0以後,ES6代碼的文件格式定爲mjs後,可以使用node --experimental-modules xxx.mjs
運行。ui
// exportDemo.mjs
export let a = 1;
複製代碼
// importDemo.mjs
import {a} from './exportDemo.mjs'
console.log(a)
複製代碼
CommonJS 模塊輸出的是一個值的拷貝(已在上一章驗證),ES6 模塊輸出的是值的引用。this
// 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模塊是編譯時輸出接口spa
Nodejs此類的運行環境會在一個閉包中運行CommonJS模塊代碼3d
(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 // 能夠改變屬性值
複製代碼
在ES6模塊中咱們更多地要去考慮語法的問題
有時候咱們會在代碼發現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() {}
複製代碼
在複雜的模塊中,可能會出現模塊間的互相引用。
// 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
時序圖下的執行步驟分解圖以下所示:
// 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
時序圖下的執行步驟分解圖以下所示:
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.mjs
bar from b.mjs
複製代碼
時序圖下的執行步驟分解圖以下所示: