Bab 12: Modul
12.1 Pengantar Modul
Bayangin kamu punya lemari besar dengan semua baju dicampur jadi satu — susah cari, susah rapiin. Modul itu kayak kamu pisahin baju ke laci-laci: laci kaos, laci celana, laci jaket. Setiap laci punya isinya sendiri, dan kamu ambil yang dibutuhkan aja.
Kenapa Perlu Modul?
Dulu, semua kode JavaScript ditaruh di satu file besar. Masalahnya:
- Variabel bisa bentrok (nama sama)
- Susah cari kode tertentu
- Susah kerja tim (semua edit file yang sama)
- Susah reuse kode
Modul menyelesaikan semua itu:
- Setiap file = satu modul
- Variabel di modul private (gak bocor keluar)
- Hanya yang di-
exportyang bisa diakses modul lain
Cara Pakai Modul di Browser
<!-- Tambahkan type="module" -->
<script type="module">
import { sapa } from './sapa.js';
sapa("Budi");
</script>Cara Pakai Modul di Node.js
Opsi 1: Pakai ekstensi .mjs
Opsi 2: Tambahkan "type": "module" di package.json
{
"type": "module"
}Fitur Modul
- Strict mode otomatis — Gak perlu tulis
"use strict" - Scope sendiri — Variabel gak bocor ke global
- Dieksekusi sekali — Mau di-import berkali-kali, kodenya cuma jalan sekali
this= undefined — Di top-level modul,thisbukanwindow
// file: counter.js
let count = 0;
export function tambah() {
count++;
return count;
}
// Mau di-import dari mana aja, count-nya SAMA (shared state)// file: main.js
import { tambah } from './counter.js';
console.log(tambah()); // 1
console.log(tambah()); // 2// file: lain.js
import { tambah } from './counter.js';
console.log(tambah()); // 3 (lanjut dari main.js!)// Di modul, variabel PRIVATE secara default
// file: rahasia.js
let password = "12345"; // Gak bisa diakses dari luar!
export let nama = "Budi"; // Ini bisa diakses12.2 Export dan Import
Named Export
// file: math.js
// Export satu per satu
export const PI = 3.14159;
export function tambah(a, b) {
return a + b;
}
export function kurang(a, b) {
return a - b;
}
// ATAU export sekaligus di bawah
const EULER = 2.71828;
function kali(a, b) {
return a * b;
}
export { EULER, kali };Named Import
// file: main.js
// Import yang dibutuhkan aja
import { PI, tambah } from './math.js';
console.log(PI); // 3.14159
console.log(tambah(2, 3)); // 5
// Import semua dengan alias
import * as math from './math.js';
console.log(math.PI); // 3.14159
console.log(math.tambah(2, 3)); // 5
console.log(math.EULER); // 2.71828Rename saat Import/Export
// Rename saat import
import { tambah as add, kurang as subtract } from './math.js';
console.log(add(2, 3)); // 5
// Rename saat export
// file: math.js
function tambah(a, b) { return a + b; }
export { tambah as add };Default Export
Setiap modul boleh punya satu default export:
// file: User.js
export default class User {
constructor(nama) {
this.nama = nama;
}
sapa() {
return `Halo, ${this.nama}!`;
}
}// file: main.js
// Import default — TANPA kurung kurawal, nama bebas
import User from './User.js';
// import Pengguna from './User.js'; // Nama bebas!
let user = new User("Budi");
console.log(user.sapa()); // "Halo, Budi!"Named vs Default Export
| Fitur | Named Export | Default Export |
|---|---|---|
| Jumlah per file | Banyak | Maksimal 1 |
| Sintaks import | import { nama } | import nama (tanpa {}) |
| Nama saat import | Harus sama | Bebas |
| Kapan pakai | Utility functions, constants | Class utama, komponen utama |
Campuran Named + Default
// file: api.js
export default class Api {
// ...
}
export const BASE_URL = "https://api.example.com";
export function formatUrl(path) {
return `${BASE_URL}${path}`;
}// file: main.js
import Api, { BASE_URL, formatUrl } from './api.js';Re-export
Berguna untuk bikin "barrel file" (satu pintu masuk):
// file: components/Button.js
export class Button { /* ... */ }
// file: components/Input.js
export class Input { /* ... */ }
// file: components/index.js — barrel file
export { Button } from './Button.js';
export { Input } from './Input.js';
// Atau: export * from './Button.js';// file: main.js — import dari satu tempat
import { Button, Input } from './components/index.js';// SALAH — import harus di top-level (gak bisa di dalam if/function)
if (kondisi) {
import { tambah } from './math.js'; // ❌ SyntaxError!
}
// SALAH — path harus string literal
let path = './math.js';
import { tambah } from path; // ❌ SyntaxError!
// Untuk kasus dinamis, pakai Dynamic Import (lihat sub-bab berikutnya)12.3 Dynamic Import
Named/default import itu kayak kamu bawa semua peralatan dari rumah sebelum berangkat kerja. Dynamic import itu kayak kamu pesan peralatan online — datangnya nanti, tapi kamu gak perlu bawa semua dari awal.
Kenapa Perlu Dynamic Import?
- Code splitting — Load kode hanya saat dibutuhkan (halaman lebih cepat)
- Conditional loading — Load modul berdasarkan kondisi
- Lazy loading — Fitur berat di-load belakangan
Sintaks
// import() mengembalikan Promise
let module = await import('./math.js');
console.log(module.tambah(2, 3)); // 5
console.log(module.PI); // 3.14159Contoh: Conditional Loading
async function muatBahasa(kode) {
let modul;
if (kode === "id") {
modul = await import('./bahasa/indonesia.js');
} else if (kode === "en") {
modul = await import('./bahasa/english.js');
}
return modul.default; // Ambil default export
}
let terjemahan = await muatBahasa("id");
console.log(terjemahan.sapa); // "Halo!"Contoh: Lazy Loading Fitur Berat
// Fitur chart cuma di-load kalau user klik tombol
document.getElementById("btnChart").addEventListener("click", async () => {
// Load library berat HANYA saat dibutuhkan
let { buatChart } = await import('./chart-library.js');
buatChart(data);
});Dynamic Import dengan Default Export
// file: Greeting.js
export default function() {
return "Halo Dunia!";
}
// file: main.js
let modul = await import('./Greeting.js');
let sapa = modul.default; // Akses default export via .default
console.log(sapa()); // "Halo Dunia!"// import() BUKAN fungsi biasa — ini sintaks khusus
// Gak bisa di-assign ke variabel:
let myImport = import; // ❌ SyntaxError!
// Tapi bisa dipanggil di mana aja (termasuk di dalam if, function, dll)
if (butuhFitur) {
let modul = await import('./fitur.js'); // ✅
}Buat sistem plugin sederhana:
- Buat 3 file "plugin":
plugin-sapa.js,plugin-hitung.js,plugin-format.js - Setiap plugin export default sebuah objek dengan properti
namadan methodjalankan() - Buat fungsi
muatPlugin(namaPlugin)yang dynamic import plugin berdasarkan nama - Jalankan plugin yang dimuat
// file: plugin-sapa.js
export default {
nama: "Sapa",
jalankan() {
return "Halo dari plugin Sapa!";
}
};
// file: main.js
async function muatPlugin(nama) {
// Kode kamu di sini
// Dynamic import berdasarkan nama
}
// Test:
let plugin = await muatPlugin("sapa");
console.log(plugin.nama); // "Sapa"
console.log(plugin.jalankan()); // "Halo dari plugin Sapa!"Sudah paham materi ini?
Tandai sebagai selesai untuk melacak progress-mu.