---
# System prepended metadata

title: Modular Programming

---

# 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