JavaScript一直沒有模塊體系,但是伴隨著ES6的到來,module隨之而來。ES6module提倡一個js文件就是一個模塊的概念,主要包括兩個命令:export和import,用於模塊向外提供介面(export)和引入其他模塊介面(import)。該隨筆分為四部分:1、ES6module概述... ...
一、module概述 |
JavaScript一直沒有模塊體系,但是伴隨著ES6的到來,module隨之而來。
ES6module的設計思想是儘量的靜態化,使得編譯時就能確定模塊的依賴關係,以及輸入、輸出變數。簡而言之就是‘編譯時載入’。
ES6module相對來說實現得還是比較簡單,易上手。
ES6module提倡一個js文件就是一個模塊的概念,主要包括兩個命令:export和import,用於模塊向外提供介面(export)和引入其他模塊介面(import)。
好了,下麵就說下我說理解的export命令和import命令。
註:學習ES6之module需要有個轉換器,因為現在好多還沒有支持ES6的module。見“ES6轉換ES5”
二、export命令 |
export命令說白了就是用於模塊對外提供的介面。
語法如下:
var f = 1; export {f};
或者
export var f = 1;
但是不能是下麵這個樣子:
var f = 1; export f;
為什麼呢?
需要註意ES6module是向外拋的值的引用,是引用。(這和CommonJS模塊不一樣,CommonJS是向外拋的值的拷貝)
所以,你像上面這樣肯定不對嘛,因為export f就是向外拋的值了,不是引用咯。
並且,還需要註意的是,export不是最後處理的哦,什麼意思?
比如test1:
export {name}; var name = 'Monkey';
如上,這樣其實向外拋的是空,如果用ES6module的import(import在下麵會詳情說到)引用它,會輸出undefined。
如下(利用import)test2:
import {name} from './test';
console.log(name);
將test1和test2,利用babel轉換成ES5後的代碼如下:
test1:
test2:
在node環境下運行test2,得下:
所以,export在ES6module中,和代碼一樣依行執行。而不像ES6module中的import那樣會將其提升到模塊頂部,首先執行。
三、import命令 |
import命令說白了,就是引用export對外提供的介面。對,是引用,而不是賦值。
import的用法如下:
/* name為test.js文件中export往外拋的引用名,名字必須一一對應 from後面的'./test'為你所引入模塊的相對路徑 */ import {name} from './test';
其中,如果你想改變引用名,可以用as,如下:
import {name as ourName} from './test';
如果你想引入模塊中的所有變數,可以利用通配符*,然後利用as自定義名稱,如下:
//引入test模塊的所有拋出的引用,並將其存放在allVar中 import * as allVar from './test'; //隨後,如果test模塊中有one方法,我們可以這麼使用 allVar.one();
像上面這些例子中,我們用import都需要明確指定模塊中對應的引用名稱,或者使用*全部引用。其實,我們還可以在export中設置default,這樣import引用模塊時,就不需要指定使用名啦。
什麼意思?
如下:
//test模塊 function getName(){ console.log('Monkey'); }; //default其實就相當於ES6module為我們設定的一個名字,對應getName的值 export default getName; //使用test模塊,one是我隨便起的名字,它就對應default,而不需要{}了 import one from './test';
四、升華 |
ES6module輸出的值是值的引用。我們可以通過一個demo,更透徹地認識這點。
如下:
//模塊sample var i = 0; export {i}; //模塊test1,引入sample模塊 import {i} from './sample'; i++; //模塊test2,引入sample模塊 import {i} from './sample'; console.log(i); //模塊main,引入test1和test2模塊 import './test1'; import './test2';
利用bable轉換:
報錯了!!在模塊test1中的i++是只讀的。(’i’ is read-only).
原因就是:由於ES6輸入的模塊變數,只是一個”符號連接“,所以這個變數是只讀的,對它進行重新賦值會報錯。(摘自‘阮一峰—ECMAScript6入門’)。
我們再修改下上面的代碼,
如下:
利用bable轉換如上代碼後,利用cmd,運行main.js,得如下結果:
我們在main.js中是利用import引入test1和test2模塊,但是,從上面的結果可以看出,他們是引用的同一份sample。
所以,ES6module輸出的值是值的引用。且,因為ES6module輸出的值是值的引用。所以當出現迴圈引用模塊時,它和CommonJS是不一樣的。
如下,CommonJS’s Demo:
註:CommonJS是值的拷貝,不是引用。
main.js
var y = require('./test'); exports.b = 'dorie'; setTimeout(function(){ console.log(y.y); },2000);
test.js
var b = require('./main'); var y; setTimeout(function(){ y = 'monkey' + b.b; },1000); exports.y = y;
運行main.js,得下結果:
Why?
因為CommonJS是值的拷貝,當執行main.js時,引入test模塊,將y的值賦值給main.js里的y變數,此時y是undefined,且已經賦值了,但test模塊在1秒後運行setTimeout里的匿名函數後,y變為’monkey’ + b.b,可是對main.js里的y已經無影響,因為是值拷貝嘛。
我們將上述代碼換成ES6module的形式,如下:
main.js
import {y} from './test'; export var b = 'dorie'; setTimeout(function(){ console.log(y); },2000);
test.js
import {b} from './main'; var y; setTimeout(function(){ y = 'monkey' + b; },1000); export {y};
利用Bable將ES6module轉換成ES5後,在node環境下運行轉換後的main.js,得如下結果:
其實邏輯與上述CommonJS是一樣的,但是結果卻不一樣,原因就是ES6module是值的引用!!