Day 16 - 了解 JavaScript Module


模組 Module

台灣翻譯叫做模組,中國翻譯叫模塊,但都是指 module

module 是用來幫助 developer 管理固定的功能,例如網站的登入頁面需要驗證使用者輸入的 email 是否符合格式,而在註冊頁面也同樣需要驗證 email 格式的功能,那就可以把這個功能寫在一個檔案裡面,當需要用到的時候就可以直接 import 使用,如此一來就可以避免在不同頁面都重複寫同樣的 code,當功能需要更改時也只要更改源頭檔案就好。

但在過去 JavaScript 沒有 module 的概念,所以 developer 社群開發非官方的模組使用方法:require 引入模組與 exports 匯出模組。


require 引入模組 與 exports 匯出模組

require

node.js 有許多內建模組,可以使用 require 引入模組並加以使用。

語法
const <self-defined name> = require('<filename>')


範例
var os = require('os'); 
// 前面變數名稱可以自己取名
// 後面 'os' 是要引入的 node.js 模組名稱

console.log(os.platform());
// 使用方法就像是物件一樣
// 直接變數名稱後面接 property 或 method 名

exports

除了引入別人寫好的模組外,自己也可以寫模組給自己用,只要將寫好的內容 exports,在別的 js 檔案裡再 require 引入即可。

語法
exports.<self-defined name> = <component>

module.exports = <component>


範例
exports 寫好的功能
  • 法一
function double(n) {
    return n * 2
}

var obj = {
    double: double,
    triple: function(n) {
        return n * 3
    }
}

module.exports = obj
// 想要輸出哪個就直接接後面
// 可想成把 obj 變成 module.exports 這物件
  • 法二
function double(n) {
    return n * 2
}

exports.double = double
exports.triple = function(n) {
    return n * 3
}
// 可想成 exports 本身就是個空物件 {}
// 只要把東西指定到物件裡就輸出了
require 引入使用
var myModule = require('./myModule')
// 引入自定義的 module 通常會放完整路徑

// 引入 module 可以想成把以下情況
// var myModule = {
//     double: double,
//     triple: function(n) {
//         return n * 3
//     }
// }

console.log(myModule.double(2), myModule.triple(10)) 
// 使用輸入的 module 
// 得到 4 30

使用 ES6 的 exportimport

ECMAScript 在 ES6 新增了 importexport 來達到 module 輸出及輸入的效果,語法相對於上述 requireexports 還要多樣,不過兩種方法都必須了解。

export

語法
// Exporting individual features
export let name1, name2, …, nameN; // also var, const
export let name1 = …, name2 = …, …, nameN; // also var, const
export function functionName(){...}
export class ClassName {...}

// Export list
export { name1, name2, …, nameN };

// Renaming exports
export { variable1 as name1, variable2 as name2, …, nameN };

// Exporting destructured assignments with renaming
export const { name1, name2: bar } = o;

// Default exports
export default expression;
export default function (…) { … } // also class, function*
export default function name1(…) { … } // also class, function*
export { name1 as default, … };

// Aggregating modules
export * from …; // does not set the default export
export * as name1 from …; // Draft ECMAScript® 2O21
export { name1, name2, …, nameN } from …;
export { import1 as name1, import2 as name2, …, nameN } from …;
export { default } from …;


import

語法
import defaultExport from "module-name";
import * as name from "module-name";
import { export } from "module-name";
import { export as alias } from "module-name";
import { export1 , export2 } from "module-name";
import { export1 , export2 as alias2 , [...] } from "module-name";
import defaultExport, { export [ , [...] ] } from "module-name";
import defaultExport, * as name from "module-name";
import "module-name";


上述為完整語法,若要簡單了解則可先知道 named export 以及 default export 兩種 export 方式:

1. named export

將寫好的 component 依照原本宣告的名稱 export,再將該 component 按原本名稱 import。此外,一個檔案中可以有多個 named export

若 named export 多個檔案,在 import 時可用 import * as <name> 一次 import 所有 component,要使用時就把它當作一個 object 來呼叫 <name>.<component>


範例
export
  • 法一
const isAdult = (age) => age >= 18;
const canDrink = (age) => age >= 18;

export { isAdult, canDrink }; 
// 把要 export 的變數放在 {} 裡面
  • 法二:可寫成 inline export
export const isAdult = (age) => age >= 18;
export const canDrink = (age) => age >= 18;
import
  • 法一:
import './person.js'
// import 特定檔案
import { isAdult, canDrink } from './person.js'
// import 檔案的特定 component

console.log(isAdult(4));
console.log(canDrink(21));
  • 法二:
import './person.js'
// import 特定檔案
import * as obj from './person.js'
// 以 import * as 一次引入所有 component
// 並把引入的 component 設為 obj 底下的 property/method

console.log(obj.isAdult(4));
console.log(obj.canDrink(21));


2. default export

一個檔案僅能有唯一的 default exportexport 時以 default export 的 component 不用給予特定名稱,且在 import 時可以設任何的名字來使用。

範例
export
  • 法一
const square = (x) => x * x;
const add = (a, b) => a + b;
const subtract = (a, b) => a - b;

export { square, add, subtract as default };
  • 法二
export const square = (x) => x * x;
export const add = (a, b) => a + b;

export default (a, b) => a - b;
import
import './utils.js';

import anythingIWant, { square, add } from './utils.js';
// 其中的 anythingIWant 就是 default 的 (a, b) => a - b
// 亦即輸出時設為 default 值,在輸入時可以設任何的名字來使用

console.log(square(4));
console.log(add(100, 23));
console.log(anythingIWant(100, 81));


總結差異:

  1. named export
    • 同個檔案可以 named export 多個 component
    • export 和 import 時要以同樣的名字進行(import 後可 renamed)
    • 在 import 時要以 { } 包住
    • 可用 import * as <name> 一次 import 所有 component
  2. default export
    • 同個檔案只能有 唯一 一個 component 是 default export
    • 在 import 時不用 { } 也不用以同樣名字 import

因為 default 一次只能設定一個,通常會將大的 component 設為 default,而其他比較小的則用 named export



其他範例
export
export function add(a,b) {
    return a + b
}

export const PI = 3.14

// 以上兩者 named export
import
import {add, PI} from './utils'
// 必須用 {} 包起來

console.log(add(3, 5), PI)

import * as whatever from './utils'
// import * 代表把所有東西都 import 進來

console.log(whatever.add(3, 5), whatever.PI)
// 使用時就要像取用 object 內容一樣


export
function add(a,b) {
    return a + b
}

const PI = 3.14

export {add as addFunction, PI}
// export 時可 rename
import
import {addFunction as a, PI} from './utils'
// import 時同樣也可 rename

console.log(a(3, 5), PI)


export
export default function add(a, b) {
    return a + b
}
// 用 default 來 export function 的話
// 可以不取名字(匿名函式)

export const PI = 3.14
import
import add, {PI} from 'utils' 
// 用 default 來 export 的東西,
// 在 import 時不用 {} 包起來,
// 且可以直接取任意名稱

console.log(add(3, 5), PI)


參考

完全解析 JavaScript import、export
模組系統
JavaScript 中 require, import 的差別及效能
一文讓你徹底搞清楚javascript中的require、import與export
ES modules: A cartoon deep-dive
Brief history of JavaScript Modules

#module #javascript #網路開發 #learning #Web Development #Front-End #前端






你可能感興趣的文章

day1- webpack

day1- webpack

VSCode Emmet: Go to Matching Pair

VSCode Emmet: Go to Matching Pair

[ CSS 02 ] 各種裝飾

[ CSS 02 ] 各種裝飾






留言討論