Sisi penawaran module sudah kita kuasai: named export untuk banyak nilai dan default export untuk satu nilai utama. Sisi pengambilan ternyata menyimpan lebih dari satu bentuk. Pernyataan import memiliki empat variasi yang masing-masing menjawab kebutuhan berbeda — mengambil beberapa nama, mengganti nama, mengambil seluruh isi module dalam satu wadah, atau sekadar memastikan sebuah module dieksekusi.

Chapter ini membahas keempatnya dalam satu contoh yang saling terkait. Toko milik Budi punya module data toko.mjs, satu module efek samping yang mencatat aktivasi, dan utama.mjs yang memakai keempat bentuk impor sekaligus.

Empat Bentuk Import #

Sebelum masuk ke kode, tabel berikut memetakan keempat bentuk pernyataan import:

BentukKegunaan
import { a, b } from './m.mjs';mengambil named export a dan b
import { a as x } from './m.mjs';mengambil named export a lalu menamainya x
import * as m from './m.mjs';mengambil seluruh named export dalam satu objek namespace
import './m.mjs';mengeksekusi module tanpa mengambil nilai apa pun

Keempatnya dapat dipakai bersamaan dalam satu berkas, dan semuanya diangkat (hoisted): Node menyelesaikan seluruh impor sebelum badan module berjalan.

Impor Bernama dan Alias as #

Bentuk dasarnya sudah kita pakai sejak chapter pertama: daftar nama di dalam kurung kurawal. Kata kunci as melengkapi bentuk ini dengan penggantian nama lokal — berguna ketika nama asli bertabrakan dengan variabel yang sudah ada, atau ketika kita ingin nama yang lebih singkat di berkas pengimpor. Nama asli di module sumber tidak berubah sama sekali; yang diganti hanyalah nama lokal di berkas pengimpor, dan nilainya tetap persis sama.

Kasus pertentangan nama paling sering muncul saat dua module menawarkan nama yang sama. Bila formatRupiah diminta dari toko.mjs, sedangkan berkas pengimpor sudah memiliki fungsi formatRupiah miliknya sendiri, kedua-duanya tidak dapat hidup berdampingan tanpa alias — salah satunya wajib diberi nama lokal baru. Dengan as, kedua fungsi tetap dapat dipakai dalam satu berkas tanpa mengubah module sumber mana pun. Kebutuhan lain yang lebih ringan: mempersingkat nama panjang yang diulang di banyak baris — penamaan ulang sekali di baris impor menghemat seluruh isi berkas.

Namespace Import: * as #

Module toko.mjs menjadi bahan uji kita. Berkas ini menawarkan tiga named export:

js toko.mjs
// toko.mjs — data dan utilitas dasar toko
export const NAMA_TOKO = "Kopi Senja";

export const daftarProduk = [
  { nama: "Kopi Susu", harga: 18000 },
  { nama: "Es Teh", harga: 8000 },
];

export function formatRupiah(angka) {
  return "Rp" + angka.toLocaleString("id-ID");
}

Ketika sebuah berkas membutuhkan hampir semua isi module, menyebut nama satu per satu menjadi melelahkan. Bentuk import * as toko mengambil seluruh named export ke dalam satu objek namespace bernama toko — nama anggotanya mengikuti nama asli di module sumber, dan aksesnya memakai notasi titik: toko.NAMA_TOKO, toko.daftarProduk, toko.formatRupiah. Apa pun yang kelak ditambahkan ke toko.mjs sebagai named export otomatis ikut muncul di namespace tanpa perlu mengubah baris impornya.

Import Efek Samping #

Bentuk keempat tidak mengambil nilai apa pun:

// penanda-aktif.mjs — module efek samping tanpa satu pun export
console.log("Modul toko diaktifkan");

Module semacam ini ditulis untuk efek jalannya kodenya sendiri: mendaftarkan sesuatu, menyiapkan konfigurasi, atau — seperti di sini — mencatat bahwa module tersebut tereksekusi. Pola ini disebut side-effect import, dan pernyataan impornya hanya berisi jalur module tanpa kurung kurawal maupun nama.

Dua sifatnya penting untuk dipegang. Pertama, module efek samping hanya dievaluasi sekali, betapapun banyak berkas yang mengimpornya — bila tiga berkas berbeda menulis import './penanda-aktif.mjs';, pesan keluarannya hanya muncul sekali pada seluruh umur program. Kedua, karena tidak menghasilkan nama apa pun, module ini tidak bisa dirujuk di badan program; nilai yang disiapkannya harus diakses lewat module lain, misalnya lewat named export dari toko.mjs.

Menyatukan Semuanya #

Berkas utama.mjs memakai keempat bentuk di atas dalam satu program. Perhatikan urutan penulisannya: module efek samping sengaja diletakkan paling atas, dan urutan inilah yang menentukan urutan evaluasi antar module:

js utama.mjs
// utama.mjs — empat bentuk import dipakai sekaligus
import './penanda-aktif.mjs';
import { NAMA_TOKO, formatRupiah as formatHarga } from './toko.mjs';
import * as toko from './toko.mjs';

console.log("Selamat datang di " + NAMA_TOKO);
console.log(formatHarga(18000));
console.log(toko.daftarProduk.length);
console.log(toko);

Output yang Dihasilkan:

text
Modul toko diaktifkan
Selamat datang di Kopi Senja
Rp18.000
2
[Module: null prototype] {
  NAMA_TOKO: 'Kopi Senja',
  daftarProduk: [
    { nama: 'Kopi Susu', harga: 18000 },
    { nama: 'Es Teh', harga: 8000 }
  ],
  formatRupiah: [Function: formatRupiah]
}

Mari kita telaah baris-baris pentingnya:

  1. Baris 2 (import './penanda-aktif.mjs';): impor efek samping dieksekusi paling awal. Keluaran pertama Modul toko diaktifkan membuktikan urutan itu — Node menyelesaikan seluruh module yang diimpor sebelum badan utama.mjs berjalan, sesuai urutan penulisannya.
  2. Baris 3 (import { NAMA_TOKO, formatRupiah as formatHarga } ...): NAMA_TOKO diambil dengan nama aslinya, sedangkan formatRupiah diberi nama lokal formatHarga lewat as.
  3. Baris 4 (import * as toko from './toko.mjs';): namespace toko dibentuk dari seluruh named export toko.mjs. Module yang sama boleh diimpor beberapa kali dengan bentuk berbeda — semua nama tersebut menunjuk module yang sama, yang hanya dievaluasi sekali. Nama toko juga menutupi perannya dengan jelas: seluruh isi berkas sumber kini dapat ditelusuri lewat satu titik.
  4. Baris 7 (console.log(formatHarga(18000));): fungsi hasil alias tetap bekerja normal — Rp18.000 terbentuk dari pemanggilan dengan nama lokal.
  5. Baris 8–9 (console.log(toko.daftarProduk.length); console.log(toko);): anggota namespace diakses lewat notasi titik. Mencetak objek namespace menampilkan wujudnya di Node: [Module: null prototype] diikuti seluruh named export beserta nilainya.

Empat bentuk impor ini menutup kebutuhan statis: Node mengetahui seluruh ketergantungan sejak program dimuat, jauh sebelum baris pertama berjalan, dan setiap nama hasil impor tercatat dengan sumbernya yang jelas. Ada situasi ketika keputusan module apa yang dimuat baru bisa diambil saat program berjalan — misalnya memilih format laporan sesuai pengaturan pengguna. Untuk kebutuhan itu, chapter berikutnya membahas impor dinamis dengan import().