爲什麼會有模塊
我們最初只會有index.js的文件,後來隨着業務的發展,這個代碼發展到了1000多行,就很難讀懂並且很難維護了了,因此我們就想到了分塊,就是把相同業務邏輯的代碼放在一起,這個就是模塊.通常是會把一個文件看作一個模塊的,每個模塊文件都有其特定的功能,便於複用。通過使用模塊,使我們能夠在開發環境上更好地開發和維護我們的項目。我在寫遊戲項目的時候我把app.js寫成element,js,enemy.js等,這個就是模塊化思想.
我們把1000多行代碼按功能分了不同的代碼塊,就是分別引用在了index.html裏,例如如下:
<script src="common.js"></script>
<script src="a.js"></script>
<script src="b.js"></script>
<script src="c.js"></script>
這樣實現模塊化存在兩大問題
- 命名衝突(不同的文件裏可能會出現相同的變量,不能實現私有化)
- 管理模塊依賴艱難(得分析模塊之間的依賴,文件數多,就會很麻煩)
因此模塊化規範誕生了,規範化的模塊會使我們統一方法定義模塊,不需要手動維護依賴。
模塊化規範
目前我們有3類模塊化規範
- CommonJs
- AMD異步模塊定義
- CMD通用模塊定義
CommonJS規範
- 文件即模塊,一個文件即模塊,每個文件擁有自己的作用域
- 使用 module.exports屬性(或簡寫exports)來暴露對外的接口和屬性
- 使用 require(moduleName) 來同步加載依賴模塊
以下爲一個例子:
math.js
/**
*創建計算圓形面積函數
*@param {Number} r 圓形面積
*@return {Number} 圓形面積
**/
functiuon area(r){
return Math.pi*r*r;
}
// 暴露對外接口
module.exports={
area:area
}
circle.js
/**
* 模塊circle
*/
var math=require('./math')
var radius=10;
// 計算圓形面積
math.area(radius);
AMD & RequireJs
CommonJs是同步加載模塊,適用在node服務端(文件存在本地硬盤),而不適合於瀏覽器端(通過網絡加載,同步加載會阻塞頁面).因此我們需要異步加載模塊,AMD(Asynchronous Module Defenition)就誕生了.
- 使用 define(id?, dependencies?, factory) 函數來定義模塊
- 使用 require(modules, callback) 函數來加載模塊
id 是模塊的標識,如果不寫,如果不寫默認就是我們加載的腳本文件的名字。
第二個參數是依賴的模塊數組,默認是[‘require’,‘exports’,‘module’].
使用方法如下:
// 沒有依賴其他模塊的定義方式
difine('moduleA',function(require,exports,module){
exports.getNum=function(){
return 5;
}
});
// 依賴其他模塊
define('moduleD',['moduleA'],function(moduleA){
// 通過模塊A的方法初始化變量index
let index=moduleA.getNum();
// 通過return來暴露addIndex方法
return {
addIndex:function(){
index+1;
}
}
})
另一個方法是require(modules,callback) 第一個參數是加載的模塊,第二個參數是要執行的函數.
require(['moduleA,moduleB'],function(moduleA,moduleB){
// 加載完模塊A和模塊B後的回調函數
})
Javascript沒有原聲的支持AMD規範,因此需要一個模塊加載器,就是requireJS.requireJS會在運行時遞歸的分析依賴,查找模塊路徑,動態插入script,監聽加載事件。
ES6的模塊化
簡單來說,ES6 模塊的設計思想就是:一個 JS 文件就代表一個 JS 模塊。在模塊中你可以使用 import 和 export 關鍵字來導入或導出模塊中的東西。
ES6 模塊主要具備以下幾個基本特點:
- 自動開啓嚴格模式,即使你沒有寫 use strict
- 每個模塊都有自己的上下文,每一個模塊內聲明的變量都是局部變量,不會污染全局作用域
- 模塊中可以導入和導出各種類型的變量,如函數,對象,字符串,數字,布爾值,類等
- 每一個模塊只加載一次,每一個 JS 只執行一次, 如果下次再去加載同目錄下同文件,直接從內存中讀取。 一個模塊就是一個單例,或者說就是一個對象
export
// 個人所得稅計算模塊
// 在線參考站點:[個人所得稅](http://www.gerensuodeshui.cn/)
// personal-income-tax.js
// 個稅起徵點
export const taxBasicNum = 3500;
// 稅率等級
export const taxRatioLevel = [
{
num: 1500, // 小於1500
ratio: '3%',
subtract: 0, // 速算扣除數
},
{
num: 4500, // 大於1500,小於4500
ratio: '10%',
subtract: 105,
},
{
num: 9000, // 大於4500,小於9000
ratio: '20%',
subtract: 555,
},
{
num: 35000, // 大於9000,小於35000
ratio: '25%',
subtract: 1005,
},
{
num: 55000, // 大於35000,小於55000
ratio: '30%',
subtract: 2755,
},
{
num: 80000, // 大於55000,小於80000
ratio: '35%',
subtract: 5505,
},
{
num: 80000, // 大於80000
ratio: '45%',
subtract: 13505,
}];
// 所繳稅收
// 應納稅所得額 = 應發工資 - 五險一金 - 個稅起徵點
// 所繳稅收 = 應納稅所得額 * 稅率 - 速算扣除數
export function calTax (num, insurance) {
let taxShouldNum = num - insurance - taxBasicNum;
let tax;
switch (true) {
case taxShouldNum < taxRatioLevel[0].num:
tax = taxShouldNum * taxRatioLevel[0].ratio - taxRatioLevel[0].subtract;
break;
case taxShouldNum < taxRatioLevel[1].num:
tax = taxShouldNum * taxRatioLevel[1].ratio - taxRatioLevel[1].subtract;
break;
case taxShouldNum < taxRatioLevel[2].num:
tax = taxShouldNum * taxRatioLevel[2].ratio - taxRatioLevel[2].subtract;
break;
case taxShouldNum < taxRatioLevel[3].num:
tax = taxShouldNum * taxRatioLevel[3].ratio - taxRatioLevel[3].subtract;
break;
case taxShouldNum < taxRatioLevel[4].num:
tax = taxShouldNum * taxRatioLevel[4].ratio - taxRatioLevel[4].subtract;
break;
case taxShouldNum < taxRatioLevel[5].num:
tax = taxShouldNum * taxRatioLevel[5].ratio - taxRatioLevel[5].subtract;
break;
case taxShouldNum > taxRatioLevel[6].num:
tax = taxShouldNum * taxRatioLevel[6].ratio - taxRatioLevel[6].subtract;
break;
default:
tax = 0;
}
return tax;
}
// 實發工資
export function calWages(num, insurance) {
let tax = calTax(num, insurance);
let wages = num - insurance - tax;
return wages;
}
import
// main.js
import {taxBasicNum, taxRatioLevel, calTax, calWages} from './personal-income-tax';
// 可以使用 taxBasicNum 輸出一段話,說明個稅的起徵點是多少
console.log(`個稅起徵點爲:taxBasicNum`);
// 還可以使用 taxRatioLevel 數據輸出一個表格,對應各個等級的稅率,這裏就不演示了
// 計算20000元繳納了五險一金3000後,應該繳納多少稅收及實際稅後工資爲多少
let tax = calTax(20000, 3000);
let wages = calWages(20000, 3000);
更詳細的介紹:
index.js
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Document</title>
</head>
<body>
<!-- module -->
<script type="module" src="main.js"></script>
</body>
</html>
module.js
// 導出方法1
export var a = 1;
export var b = 2;
// 導出方法2
var c=3;
var d=4;
export { c,d };
export { c as myC,d as myD }
// 導出方法3
export default []
import arr,{ a,b,c,d,myC,myD } from './module.js';
console.log(a);
console.log(b);
// 第二種方式
console.log(c);
console.log(d);
// 第二種導入方式的另一種用法
console.log(myC);
console.log(myD);
// 第3種導入方法
console.log(arr);
// 第4種整體倒入 正常導入
import * as m from './module.js';
console.log(m);
// 異步導入
import ('./module.js').then(function(m){
console.log(m);
});
我個人覺得應該儘量使用ES6的export,import 而不是AMD 和CMD,因爲requireJs會使性能變慢.