JavaScript

Belum masuk. Progresmu tidak tersimpan.

Masuk
  • Bab 10 dari 12
  • 30 menit
  • 1 latihan kode
  • 4 soal kuis

Class dan Modules

Di bab ini (8 bagian)
  1. Tujuan
  2. Class itu cetakan, objek itu kuenya
  3. Getter dan setter: properti yang sebenarnya fungsi
  4. Static: milik class, bukan milik instance
  5. extends dan super: mewarisi tanpa menyalin
  6. Modules: satu file, satu tanggung jawab
  7. Kesalahan umum
  8. Rangkuman

Tujuan bab ini

  • Membuat class lengkap dengan constructor, method, getter/setter, dan static.
  • Mewariskan class dengan extends dan memanggil induknya lewat super.
  • Memecah kode ke beberapa file dengan export dan import, termasuk beda named dan default export.

Class itu cetakan, objek itu kuenya

Di bab objek kamu sudah bikin objek satu per satu dengan { }. Masalahnya muncul saat kamu butuh puluhan objek dengan bentuk yang sama, misalnya data mahasiswa. Menyalin { nama, nim, perkenalan() {...} } berkali-kali itu capek dan rawan typo.

class (hadir di ES6 atau ES2015) adalah cetakan. Kamu tulis bentuknya sekali, lalu cetak objek sebanyak apa pun dengan new. Objek hasil cetakan disebut instance.

class Mahasiswa {
  constructor(nama, nim) {
    this.nama = nama;
    this.nim = nim;
  }

  perkenalan() {
    return `Saya ${this.nama}, NIM ${this.nim}`;
  }
}

const dini = new Mahasiswa("Dini", "0421");
const raka = new Mahasiswa("Raka", "0422");

console.log(dini.perkenalan()); // Saya Dini, NIM 0421
console.log(raka.nama);         // Raka

Yang terjadi saat new Mahasiswa("Dini", "0421") dijalankan, langkah demi langkah:

  1. JavaScript membuat objek kosong baru.
  2. constructor dipanggil otomatis, dan this menunjuk ke objek baru itu.
  3. Baris this.nama = nama mengisi properti objek.
  4. Objek yang sudah terisi dikembalikan dan disimpan ke dini.

Method seperti perkenalan() tidak disalin ke tiap objek. Ia disimpan sekali di Mahasiswa.prototype, lalu dipakai bersama oleh semua instance. Itulah kenapa class disebut "gula sintaksis": di balik layar, JavaScript tetap memakai sistem prototype yang sudah ada sejak dulu. Class hanya membuat penulisannya lebih rapi dan familiar bagi yang pernah belajar Java atau Python.

console.log(dini.perkenalan === raka.perkenalan); // true, fungsi yang sama
console.log(typeof Mahasiswa);                    // "function"

Dua hal kecil yang beda dari fungsi biasa: class wajib dipanggil dengan new (tanpa new kamu dapat TypeError), dan isi class otomatis berjalan dalam strict mode.

Getter dan setter: properti yang sebenarnya fungsi

Kadang sebuah nilai lebih masuk akal dihitung daripada disimpan. Luas lingkaran, misalnya, selalu bergantung pada jari-jarinya. Getter (get) membuatmu bisa membaca hasil hitungan seperti properti biasa, tanpa tanda kurung.

class Lingkaran {
  constructor(r) {
    this._r = r;
  }

  get luas() {
    return Math.round(Math.PI * this._r ** 2);
  }

  set jari(nilai) {
    if (nilai <= 0) throw new Error("Jari-jari harus positif");
    this._r = nilai;
  }
}

const roda = new Lingkaran(7);
console.log(roda.luas); // 154
roda.jari = 10;         // memanggil setter
console.log(roda.luas); // 314
roda.jari = -1;         // Error: Jari-jari harus positif

Setter (set) dipanggil saat kamu menulis dengan =. Ini tempat yang pas untuk validasi. Awalan _ pada _r hanyalah konvensi: tanda "ini urusan internal, jangan diutak-atik dari luar". JavaScript tidak benar-benar melarangnya. Properti privat resmi dengan tanda # baru datang di ES2022, dan kita bahas di bab JavaScript modern.

Static: milik class, bukan milik instance

Method static menempel pada class itu sendiri. Cocok untuk fungsi bantu yang tidak butuh data dari satu objek tertentu.

class Kalkulator {
  static tambah(a, b) {
    return a + b;
  }
}

console.log(Kalkulator.tambah(2, 3)); // 5

const k = new Kalkulator();
console.log(k.tambah); // undefined, instance tidak punya

Contoh nyata yang sudah sering kamu pakai: Math.max(), Array.from(), dan Number.isInteger() semuanya method static.

extends dan super: mewarisi tanpa menyalin

Misalkan mahasiswa Universitas Terbuka punya semua sifat Mahasiswa, ditambah program studi. Daripada menulis ulang, pakai extends.

class MahasiswaUT extends Mahasiswa {
  constructor(nama, nim, prodi) {
    super(nama, nim); // jalankan constructor induk dulu
    this.prodi = prodi;
  }

  perkenalan() {
    return `${super.perkenalan()}, prodi ${this.prodi}`;
  }
}

const sari = new MahasiswaUT("Sari", "0533", "Sistem Informasi");
console.log(sari.perkenalan());
// Saya Sari, NIM 0533, prodi Sistem Informasi
console.log(sari instanceof Mahasiswa); // true

super punya dua wajah:

BentukDipakai diArtinya
super(...)constructor class anakmemanggil constructor induk
super.method()method mana punmemanggil versi method milik induk

Aturan penting: di class anak, super() harus dipanggil sebelum kamu menyentuh this. Alasannya, objeknya baru benar-benar dibuat oleh constructor induk. Menulis this.prodi sebelum super() langsung menghasilkan ReferenceError.

Menulis ulang perkenalan() di class anak disebut override. Versi anak yang dipakai, tapi lewat super.perkenalan() kamu masih bisa meminjam hasil versi induk lalu menambahinya.

Modules: satu file, satu tanggung jawab

Proyek nyata tidak ditulis dalam satu file raksasa. ES6 Modules membuat tiap file punya ruang lingkup sendiri: variabel di satu file tidak bocor ke file lain kecuali kamu export, dan file lain baru bisa memakainya kalau ia import.

File pertama, math.js:

export const PI = 3.14;

export function keliling(r) {
  return 2 * PI * r;
}

export default function luas(r) {
  return PI * r * r;
}

File kedua, app.js:

import luas, { PI, keliling as kel } from "./math.js";

console.log(luas(10)); // 314
console.log(PI);       // 3.14
console.log(kel(10));  // 62.800000000000004

Bedanya named dan default export:

Named exportDefault export
Jumlah per fileboleh banyakmaksimal satu
Cara importpakai { }, nama harus samatanpa { }, nama bebas
Ganti nama{ keliling as kel }langsung import apaSaja from
Ambil semuaimport * as math from "./math.js"ada di math.default

Beberapa sifat modul yang perlu kamu tahu:

  • Modul dimuat sekali lalu di-cache. Kalau lima file meng-import math.js, kodenya tetap jalan satu kali.
  • Modul selalu strict mode.
  • Di browser, file utama dipanggil dengan <script type="module" src="app.js"></script>. Di Node.js, pakai ekstensi .mjs atau "type": "module" di package.json.
  • Karena import/export bersifat statis, bundler seperti Vite atau Webpack bisa membuang fungsi yang tidak pernah di-import. Proses ini disebut tree-shaking.

Catatan untuk latihan di bawah: editor latihan menjalankan satu file saja, jadi tidak ada import. Kamu cukup menulis class-nya langsung.

Kesalahan umum

  • Lupa new. Mahasiswa("Dini") melempar TypeError: Class constructor Mahasiswa cannot be invoked without 'new'.
  • Memakai this sebelum super(). Di constructor class anak, panggil super(...) di baris pertama.
  • Memanggil getter dengan kurung. roda.luas() error karena luas hasilnya angka, bukan fungsi. Tulis roda.luas.
  • Memanggil method static dari instance. k.tambah(2, 3) gagal; yang benar Kalkulator.tambah(2, 3).
  • Tertukar kurung kurawal saat import. import { luas } from "./math.js" gagal kalau luas diekspor sebagai default. Default diimpor tanpa { }.
  • Lupa ekstensi .js pada path import di browser. Browser butuh path lengkap seperti "./math.js".

Rangkuman

  • class adalah cetakan objek; constructor jalan otomatis saat new, dan method disimpan bersama di prototype.
  • Getter dan setter membuat hitungan dan validasi terasa seperti properti biasa; static dipanggil dari class, bukan instance.
  • extends mewariskan class, super() memanggil constructor induk dan wajib datang sebelum this.
  • Modules memecah kode per file: named export bisa banyak dan diimpor pakai { }, default export cuma satu dan diimpor tanpa { }.

Selesaikan bab ini

  1. 1. Baca materi
  2. 2. Latihan kode (1)
  3. 3. Kuis (4 soal)

Latihan kode: Produk dan produk diskon

Buat dua class:

  • Produk dengan constructor(nama, harga), method info() yang mengembalikan "<nama>: Rp<harga>", dan method static termurah(a, b) yang mengembalikan produk dengan harga lebih kecil.
  • ProdukDiskon yang extends Produk, dengan constructor(nama, harga, persen) (panggil super). Tambahkan getter hargaAkhir yang mengembalikan harga - harga * persen / 100, dan override info() menjadi "<nama>: Rp<hargaAkhir> (diskon <persen>%)".

Contoh: new ProdukDiskon("Kaos", 100000, 20).info() menghasilkan "Kaos: Rp80000 (diskon 20%)".

Cek pemahaman

  1. 1Apa yang ditampilkan kode berikut?

    class Mahasiswa {
      constructor(nama) { this.nama = nama; }
      perkenalan() { return `Saya ${this.nama}`; }
    }
    class MahasiswaUT extends Mahasiswa {
      constructor(nama, prodi) {
        super(nama);
        this.prodi = prodi;
      }
      perkenalan() { return `${super.perkenalan()} (${this.prodi})`; }
    }
    console.log(new MahasiswaUT("Sari", "SI").perkenalan());
  2. 2Di constructor class anak, kapan `super(...)` harus dipanggil?

  3. 3Method `static` bisa dipanggil langsung dari instance, misalnya `new Kalkulator().tambah(2, 3)`.

  4. 4`math.js` berisi `export default function luas(r) {...}`. Import mana yang benar?