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?
- Mesin JavaScript memeriksa apakah properti tersebut ada langsung di dalam objek
daftaritu sendiri. - Jika tidak ada, mesin menelusuri rantai prototipe (Prototype Chain) ke atas menuju
Array.prototype, lalu keObject.prototype, hingga berakhir dinull.
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(), atauObject.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));
}
}
super() di dalam Constructor SubkelasJika 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:
- Buat class
Tugasdengan field privat#status = "menunggu", properti publikiddanjudul, getterstatus, serta metodeselesaikan()yang mengubah#statusmenjadi"selesai". - Buat subkelas
TugasBerjadwal extends Tugasyang menambahkan propertitenggatWaktudi dalamconstructor(id, judul, tenggatWaktu)menggunakansuper(id, judul). - Uji pembuatan instans dengan
new TugasBerjadwal(...)dan pastikan#statustidak dapat diubah secara paksa dari luar class.