# Modular Programming **模組開發(Modular Programming):** 主要分成兩種方式,分別是同步載入的(CommonJS)和非同步載入的(AMD)。 **使用模組開發的理由:** 隨著網站越加的複雜,JS檔案的命名空間和依賴關係就變得越難以處理,於是模組誕生了。 * ~~隱藏私有的實作細節(encapsulating)~~ 代碼方便管理 * ~~維持全域命名空間(implementation details)~~ 避免命名衝突&全域污染 * 來源各異的程式碼模組可以相結合(moudlar programming) ```+ //不使用模組的原生JS程式碼 <body> <script src="./module1.js"></script> <script src="./module2.js"></script> <script src="./module3.js"></script> </body> ``` ## AMD vs CommonJS vs ES6 ECMA在2015年推出ES6前,原生的JS是不支援模組的,於是先有了CommonJS。 (但雖然ES6已經新增了模組,但瀏覽器們普遍還無法支援,因此需要類似Babel的工具將程式碼轉換成瀏覽器能接受的ES5~) ### **CommonJS** 最初是以定義一系列規範,藉此幫助後端伺服器為目標開發的,因此在前後端切換時也較為節省效能。語法較相似於一般程式語言,採用同步載入模組,NodeJS最初就是以CommonJS為核心開發的。 ```+ // module1.js let counter = 5; module.exports = { counter }; //module.exports導出模組 ``` ```+ //index.js const module1 = require('./module1'); //require匯入模組 ``` ### **AMD(Asynchronous Module Definition)** 誕生於對CommonJS的不滿,最大的不同就是採用非同步的方式載入模組,讓瀏覽器的畫面載入時使用者體驗更好,但是其語法較為複雜冗長不易上手,代表的實踐者為RequireJS。 ```+ // store.js文件 // 定義無依賴模塊 define(function () { let msg = '我在store.js裡' function getMsg() { return msg } // 暴露模块 return { getMsg } }) ``` ```+ // alerter.js文件 // 定義有依賴模塊 define([ 'store', ], function(store) { let addMsg = '我被alert添加了' function showMsg() { alert(store.getMsg() + ', ' + addMsg) } return { showMsg } }); ``` ```+ // main.js文件 (function () { // 配置require require.config({ baseUrl: 'js/', paths: { // 映射,標註模塊名子(依賴時的數組裡的名子就是這個) alerter: './alerter', // 不能寫成alter.js會報錯誤 store: './store' // 導入第三方庫 // jquery: './libs/jquery-1.10.1' //注意:寫成jQuery會報錯 } }) require(['alerter'], function (alerter) { alerter.showMsg() }) })() ``` ```+ //index.html <body> <!-- 引入require.js並指定js主文件的入口 --> <script data-main="main" src="lib/require.js"></script> </body> ``` ### **ES6 Module** 以CommmonJS為基礎,與前兩者不同,可以兼容同步和非同步的模組,屬於靜態模組(可直接藉由類似webpack的工具打包成一個檔案;反之為動態模組,模組須由瀏覽器自行載入)在ES6模組中的程式碼會自動處於嚴格模式,甚至會更嚴格!(如位在頂層的this會是undefined而非全域物件) ```+ //module1.js const module1 = ()=>{ let example = "hi" } export default module1 //寫法1 export {module1} //寫法2 ``` ```+ //index.js import module1 from "./modules" //對應寫法1 import {module1} from "./modules" //對應寫法2 console.log(example) //export,import一定要處於模組頂層,假如出現在塊級作用域{}or()就會報錯! //模組路徑一定要使用"./"或"./"的相對路徑 ``` ## 快速複習 ![](https://i.imgur.com/d94P066.png) ![](https://i.imgur.com/Vj0RY2O.png) ## 參考資料 https://auth0.com/blog/javascript-module-systems-showdown/#Introduction--Why-Are-JavaScript-Modules-Needed- https://ithelp.ithome.com.tw/articles/10191574 https://es6.ruanyifeng.com/#docs/module