ES6以前,一個Web應用的每一個JS文件所定義的全部內容都由全局做用域共享。當Web應用變得愈來愈複雜,須要更多的JS代碼時,此種方式會致使命名衝突、安全等不少問題。javascript
如何解決?html
ES6的設計目標之一就是要解決做用域問題,並讓JS應用變得更有調理。 這即是模塊的切入點。java
模塊( Modules )是使用不一樣方式加載的 JS 文件(與 JS 原先的腳本加載方式相對)es6
簡單來講,能夠認爲一個模塊就是一個js文件,該模塊中有變量、函數、類。
→模塊(Modules)與腳本(Script)的語義有很大的不一樣:
一、模塊代碼自動運行在嚴格模式下,而且沒有任何辦法跳出嚴格模式;
二、在模塊的頂級做用域建立的變量,不會自動添加到共享的全局做用域,它們只會在模塊頂級做用域的內部存在
三、模塊頂級做用域的this值爲undefined;
四、模塊不容許在代碼中使用HTML風格的註釋;
五、對於須要讓模塊外部訪問的內容,模塊必須導出它們;
六、容許模塊從其餘模塊導入綁定;
web
function add(a, b) { return a + b; } function sub(a, b) { return a - b; } function multi(a, b) { return a * b; } function divide(a, b) { return a / b; }
//先定義,後導出 export { add, sub, multi, divide }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <!--告訴瀏覽器 要將內聯代碼當作模塊,這是一個直接嵌入到網頁內的模塊--> <script type="module"> import { add, sub, multi, divide } from './modules/calc.js'; let result = add(10, 20); console.log('result=' + result); </script> </head> <body> </body> </html>
在這裏,result變量沒有暴露到全局,由於它只在<script>元素定義的這個模塊內部存在,所以也沒有被添加爲window對象的屬性。瀏覽器
輸出結果爲:
安全
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <!--告訴瀏覽器 要將指定文件中的代碼當作模塊,而不是當作腳本,這裏使用src加載了外部模塊文件--> <script type="module" src="./modules/calc.js"></script> </head> <body> </body> </html>
模塊相對腳本的獨特之處在於:它們能使用 import 來指定必需要加載的其餘文件,以保證正確執行。爲了支持此功能, <script type="module"> 老是表現得像是已經應用了 defer 屬性。ide
defer 屬性是加載腳本文件時的可選項,但在加載模塊文件時老是自動應用的。當 HTML 解析到擁有 src 屬性的 <script type="module"> 標籤時,就會當即開始下載模塊文件,但並不會執行它,直到整個網頁文檔所有解析完爲止。
模塊也會按照它們在 HTML 文件中出現的順序依次執行,這意味着第一個 <script type="module"> 老是保證在第二個以前執行,即便其中有些模塊不是用 src 指定而是包含了內聯腳本。函數
例如:
第一步:新建一個模塊foo.js在modules目錄下,foo.js內容以下:測試
function hello(){ console.log('foo.hello...'); } console.log('foo.js模塊執行了');
第二步:新建測試網頁foo.html,內容以下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <!--外部文件方式加載模塊--> <!--first執行--> <script type="module" src="./modules/foo.js"></script> <!--second執行--> <script type="module"> console.log('second模塊執行了...'); </script> </head> <body> <h1>Foo</h1> <script type="text/javascript"> //模擬加載數據耗時2秒 setTimeout(() => { console.log('網頁模擬加載數據執行了...'); }, 2000); </script> <!--third執行--> <script type="module"> console.log('third模塊執行了...'); </script> </body> </html>
觀察執行結果:
ES6 爲 JS 語言添加了模塊,做爲打包與封裝功能的方式。
模塊的行爲異於腳本,它們不會用自身頂級做用域的變量、函數或類去修改全局做用域,而模塊的 this 值爲 undefined 。爲了實現這些行爲,模塊在被加載時使用了一種不一樣的方式。
你必須將模塊中須要向外提供的任何功能都導出,變量、函數與類均可以,而且每一個模塊容許存在一個默認導出。
在導出以後,另外一個模塊就能導入該模塊所導出的一個或多個名稱了。這些導入的名稱就像是被 let 所定義的,會被看成塊級綁定,而且不允在同一模塊內重複聲明。
因爲模塊必須用與腳本不一樣的方式運行,瀏覽器就引入了 <script type="module"> ,以表示資源文件或內聯代碼須要做爲模塊來執行。
使用 <script type="module"> 加載的模塊文件會默認應用 defer 屬性。一旦包含模塊的頁面文檔徹底被解析,模塊就會按照它們在文檔中的出現順序依次執行。
參考資料:《Understanding ECMAScript 6》,做者:Nicholas C. Zakas ,在線閱讀地址:https://leanpub.com/understandinges6/read#leanpub-auto-what-are-modules