16.11.11node
過了又一個關棍節,感受。。。。很差es6
參考:阮一峯ES6緩存
CommonJS對模塊的定義很是簡單,主要分爲模塊引用、模塊定義和模塊標誌babel
var math = reqiure('math');
require()接受模塊的標誌符。以此引入一個模塊的API到當前的上下文中。函數
上下文提供exports
對象用於導出當前模塊的方法和變量,而且它是惟一導出的出口。module
對象,它表明模塊自身。而exports是module的屬性。ui
模塊標識就是傳遞給require()方法的參數。必須符合小駝峯命名的字符串。設計
CommonJS的模塊導出和引用機制使得用戶徹底沒必要考慮變量污染。code
ES6模塊的設計思想,是儘可能的靜態化,使得編譯時就能肯定模塊的依賴關係,以及輸入和輸出的變量。CommonJS和AMD模塊,都只能在運行時肯定這些東西。對象
// CommonJS模塊 let { stat, exists, readFile } = require('fs'); // 等同於 這種方式稱爲「運行時加載」 let _fs = require('fs'); let stat = _fs.stat, exists = _fs.exists, readfile = _fs.readfile;
exports
&& module.exports
繼承
直接複製給exports,會報錯。
exports= function(){ // }
由於exports對象是經過形參的方式傳入的,直接賦值形參會改變形參的做用,但並不能改變做用域外的值。
若是要達到require引入一個類的效果,請賦值給module.exports對象,這個迂迴的方案不改變形參的引用。
須要特別注意的是,export命令規定的是對外的接口,必須與模塊內部的變量創建一一對應關係。
使用export命令定義了模塊的對外接口之後,其餘JS文件就能夠經過import命令加載這個模塊(文件)。
import命令接受一個對象(用大括號表示),裏面指定要從其餘模塊導入的變量名。大括號裏面的變量名,必須與被導入模塊s對外接口的名稱相同。
除了指定加載某個輸出值,還可使用總體加載,即用星號(*)指定一個對象,全部輸出值都加載在這個對象上面。
export default 一個模塊只有一個。爲模塊指定默認輸出。
使用import命令的時候,用戶須要知道所要加載的變量名或函數名,不然沒法加載。可是,用戶確定但願快速上手,未必願意閱讀文檔,去了解模塊有哪些屬性和方法。
其餘模塊加載該模塊時,import命令能夠爲該匿名函數指定任意名字。
如下比較默認輸出和正常輸出:
// 輸出 export default function crc32() { // ... } // 輸入 import crc32 from 'crc32'; // 輸出 export function crc32() { // ... }; // 輸入 import {crc32} from 'crc32';
本質上,export default就是輸出一個叫作default的變量或方法,而後系統容許你爲它取任意名字。
CommonJS模塊的是一個值的拷貝,而ES6模塊輸出的是值的引用。
ES6模塊加載的機制,與CommonJS模塊徹底不一樣。CommonJS模塊輸出的是一個值的拷貝,而ES6模塊輸出的是值的引用。
CommonJS模塊輸出的是被輸出值的拷貝,也就是說,一旦輸出一個值,模塊內部的變化就影響不到這個值。
ES6模塊的運行機制與CommonJS不同,它遇到模塊加載命令import時,不會去執行模塊,而是隻生成一個動態的只讀引用。等到真的須要用到時,再到模塊裏面去取值,換句話說,ES6的輸入有點像Unix系統的「符號鏈接」,原始值變了,import輸入的值也會跟着變。所以,ES6模塊是動態引用,而且不會緩存值,模塊裏面的變量綁定其所在的模塊。
CommonJS的一個模塊,就是一個腳本文件。require命令第一次加載該腳本,就會執行整個腳本,而後在內存生成一個對象。
{ id: '...', exports: { ... }, loaded: true, ... }
該對象的id屬性是模塊名,exports屬性是模塊輸出的各個接口,loaded屬性是一個布爾值,表示該模塊的腳本是否執行完畢。
之後須要用到這個模塊的時候,就會到exports屬性上面取值。即便再次執行require命令,也不會再次執行該模塊,而是到緩存之中取值。也就是說,CommonJS模塊不管加載多少次,都只會在第一次加載時運行一次,之後再加載,就返回第一次運行的結果,除非手動清除系統緩存。
<mark>CommonJS模塊的重要特性是加載時執行,即腳本代碼在require的時候,就會所有執行。一旦出現某個模塊被"循環加載",就只輸出已經執行的部分,還未執行的部分不會輸出。</mark>
// a.js exports.done = false; var b = require('./b.js'); console.log('在 a.js 之中,b.done = %j', b.done); exports.done = true; console.log('a.js 執行完畢'); //b.js exports.done = false; var a = require('./a.js'); console.log('在 b.js 之中,a.done = %j', a.done); exports.done = true; console.log('b.js 執行完畢'); //main.js var a = require('./a.js'); var b = require('./b.js'); console.log('在 main.js 之中, a.done=%j, b.done=%j', a.done, b.done); //執行main.js $ node main.js 在 b.js 之中,a.done = false b.js 執行完畢 在 a.js 之中,b.done = true a.js 執行完畢 在 main.js 之中, a.done=true, b.done=true
ES6處理「循環加載」與CommonJS有本質的不一樣。ES6模塊是動態引用,若是使用import從一個模塊加載變量(即import foo from 'foo'),那些變量不會被緩存,而是成爲一個指向被加載模塊的引用,須要開發者本身保證,真正取值的時候可以取到值。
// a.js以下 import {bar} from './b.js'; console.log('a.js'); console.log(bar); export let foo = 'foo'; // b.js import {foo} from './a.js'; console.log('b.js'); console.log(foo); export let bar = 'bar'; $ babel-node a.js b.js undefined a.js bar
上面說過,const聲明的常量只在當前代碼塊有效。若是想設置跨模塊的常量(即跨多個文件),能夠採用下面的寫法。
// constants.js 模塊 export const A = 1; export const B = 3; export const C = 4; // test1.js 模塊 import * as constants from './constants'; console.log(constants.A); // 1 console.log(constants.B); // 3 // test2.js 模塊 import {A, B} from './constants'; console.log(A); // 1 console.log(B); // 3