Kodeloka
  1. Kodeloka
  2. Kelas JavaScript
  3. Materi

2 · Objek & Data · Modul 5 dari 22

Prototipe, Sintaks class, Field Privat (#), dan Pewarisan (extends)

Memahami bagaimana pewarisan prototipe (prototypal inheritance) bekerja di balik layar, merancang cetak biru objek dengan sintaks class, menyembunyikan data internal dengan private fields (#), serta menerapkan static, getter/setter, dan extends / super.

1. Bagaimana Objek di JavaScript Saling Mewarisi? (Prototypal Inheritance)

Berbeda dengan bahasa seperti Java atau C++ klasik di mana class adalah cetak biru terpisah dari objek, di JavaScript setiap objek memiliki tautan internal tersembunyi ([[Prototype]]) yang menunjuk ke objek lain yang disebut prototipenya.

Ketika Anda memanggil teks.toUpperCase() atau daftar.map(...), dari mana metode .toUpperCase() dan .map() itu berasal?

  1. Mesin JavaScript memeriksa apakah properti tersebut ada langsung di dalam objek daftar itu sendiri.
  2. Jika tidak ada, mesin menelusuri rantai prototipe (Prototype Chain) ke atas menuju Array.prototype, lalu ke Object.prototype, hingga berakhir di null.

Sintaks class yang diperkenalkan sejak ES2015 adalah cara yang bersih, terstruktur, dan modern untuk mendefinisikan konstruktor beserta metode-metode di dalam prototype-nya!

2. Anatomi class Modern dan Enkapsulasi Sejati dengan # (Private Fields)

Sebelum ES2022, pengembang JavaScript terpaksa menandai properti internal dengan garis bawah semu seperti this._saldo, padahal properti tersebut tetap bisa diubah sembarangan dari luar objek.

Kini (status Baseline Widely Available), awalan tanda pagar # menciptakan Private Fields dan Private Methods sejati yang ditegakkan langsung oleh mesin JavaScript: upaya mengakses #properti dari luar blok class akan langsung ditolak sebagai SyntaxError!

class AkunLayanan {
  // 1. Public field dengan nilai awal
  peran = "standar";

  // 2. Private field (wajib dideklarasikan di tingkat atas class dengan awalan #)
  #kunciApi;
  #kuotaTersisa;

  constructor(namaLayanan, kunciApi, kuotaAwal = 1000) {
    this.namaLayanan = namaLayanan;
    this.#kunciApi = kunciApi;
    this.#kuotaTersisa = kuotaAwal;
  }

  // 3. Getter untuk membaca nilai privat tanpa mengizinkan pengubahan sembarangan
  get kuota() {
    return this.#kuotaTersisa;
  }

  // 4. Private method (hanya bisa dipanggil dari dalam class ini)
  #formatHeaderOtorisasi() {
    return `Bearer ${this.#kunciApi}`;
  }

  pakaiKuota(jumlah = 1) {
    if (jumlah > this.#kuotaTersisa) {
      throw new Error(`Kuota layanan ${this.namaLayanan} habis.`);
    }
    this.#kuotaTersisa -= jumlah;
    return {
      header: this.#formatHeaderOtorisasi(),
      sisa: this.#kuotaTersisa
    };
  }

  // 5. Memeriksa apakah suatu objek merupakan instans yang memiliki field #kunciApi (ergonomic brand check)
  static adalahAkunSah(objek) {
    return #kunciApi in objek;
  }
}

const akun = new AkunLayanan("Worker-Email", "rahasia-xyz-99", 500);
console.log(akun.kuota); // 500 (Memanggil getter 'get kuota()')
// console.log(akun.#kunciApi); // SyntaxError: Private field '#kunciApi' must be declared in an enclosing class

3. Metode dan Properti static

Kata kunci static mendefinisikan properti atau metode yang menempel langsung pada Class itu sendiri, bukan pada masing-masing objek instans yang dibuat dengan new.

Metode static sangat cocok digunakan untuk:

  • Factory Methods: Membuat instans objek dari berbagai format masukan (misalnya Pengguna.dariJson(str)).
  • Fungsi Utilitas atau Konstanta Kelas: Misalnya Number.isNaN(), Array.isArray(), atau Object.groupBy().
class KonfigurasiDatabase {
  static PORT_BAWAAN = 5432;

  constructor(host, port = KonfigurasiDatabase.PORT_BAWAAN) {
    this.host = host;
    this.port = port;
  }

  // Static Factory Method
  static dariUrl(urlString) {
    const url = new URL(urlString);
    const port = url.port ? Number(url.port) : KonfigurasiDatabase.PORT_BAWAAN;
    return new KonfigurasiDatabase(url.hostname, port);
  }
}

const db = KonfigurasiDatabase.dariUrl("postgres://db.kodeloka.internal:6432/utama");
console.log(db.host, db.port); // "db.kodeloka.internal" 6432

4. Pewarisan Kelas dengan extends dan super

Gunakan kata kunci extends untuk membuat subkelas (child class) yang mewarisi perilaku kelas induk (parent class), dan gunakan super untuk memanggil konstruktor atau metode milik kelas induk:

class PenyimpananDasar {
  constructor(awalanKunci) {
    this.awalanKunci = awalanKunci;
  }

  formatKunci(kunci) {
    return `${this.awalanKunci}:${kunci}`;
  }
}

class PenyimpananMemori extends PenyimpananDasar {
  #data = new Map();

  constructor(awalanKunci, kapasitasMaks = 100) {
    // WAJIB memanggil super(...) terlebih dahulu sebelum mengakses 'this' di dalam constructor anak!
    super(awalanKunci);
    this.kapasitasMaks = kapasitasMaks;
  }

  simpan(kunci, nilai) {
    if (this.#data.size >= this.kapasitasMaks) {
      throw new Error("Kapasitas penyimpanan penuh.");
    }
    // Memanggil metode milik kelas induk dengan super.formatKunci()
    const kunciLengkap = super.formatKunci(kunci);
    this.#data.set(kunciLengkap, nilai);
  }

  ambil(kunci) {
    return this.#data.get(super.formatKunci(kunci));
  }
}
⚠️ Aturan Wajib super() di dalam Constructor Subkelas

Jika sebuah kelas menggunakan extends dan mendefinisikan constructor() miliknya sendiri, ia wajib memanggil super(...) sebelum menggunakan kata kunci this. Jika Anda mencoba menulis this.kapasitasMaks = 100 sebelum baris super(), JavaScript akan melempar ReferenceError: Must call super constructor in derived class before accessing 'this'.

Latihan Praktik: Merancang Sistem Antrean Tugas Berorientasi Objek

Buatlah berkas antrean-tugas.js dan jalankan dengan Node.js:

  1. Buat class Tugas dengan field privat #status = "menunggu", properti publik id dan judul, getter status, serta metode selesaikan() yang mengubah #status menjadi "selesai".
  2. Buat subkelas TugasBerjadwal extends Tugas yang menambahkan properti tenggatWaktu di dalam constructor(id, judul, tenggatWaktu) menggunakan super(id, judul).
  3. Uji pembuatan instans dengan new TugasBerjadwal(...) dan pastikan #status tidak dapat diubah secara paksa dari luar class.