ES6之前,一個Web應用的每個JS檔案所定義的所有內容都由全域作用域共享,當Web應用變得越來越復雜,需要更多的JS代碼時,此種方式會導致命名沖突、安全等很多問題,
如何解決?
ES6的設計目標之一就是要解決作用域問題,并讓JS應用變得更有調理, 這便是模塊的切入點,
- 什么是模塊(What are Modules)?
Modules are JavaScript files that are loaded in a different mode (as opposed to scripts, which are loaded in the original way JavaScript worked).
模塊( Modules )是使用不同方式加載的 JS 檔案(與 JS 原先的腳本加載方式相對)
簡單來說,可以認為一個模塊就是一個js檔案,該模塊中有變數、函式、類,
我們這里定義一個模塊檔案(calc.js)放到modules目錄下,內容如下:
→模塊(Modules)與腳本(Script)的語意有很大的不同:
1、模塊代碼自動運行在嚴格模式下,并且沒有任何辦法跳出嚴格模式;
2、在模塊的頂級作用域創建的變數,不會自動添加到共享的全域作用域,它們只會在模塊頂級作用域的內部存在
3、模塊頂級作用域的this值為undefined;
4、模塊不允許在代碼中使用HTML風格的注釋;
5、對于需要讓模塊外部訪問的內容,模塊必須匯出它們;
6、允許模塊從其他模塊匯入系結;
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 }
- 如何加載模塊?
ES6規范中定義了模塊的語法以及抽象的加載機制,但沒有定義如何加載他們,因此具體的實作環境(例如web瀏覽器、Node.js)可以自行決定用什么方式實作,以便契合各自的環境,
→在Web瀏覽器中使用模塊(使用script標簽)
方式一:<!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 屬性,
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
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/245119.html
標籤:JavaScript
