Ketika data dan perilaku selalu berjalan bersama — tugas yang punya judul, durasi, dan cara menandai selesai — membentuknya sebagai object lepas menjadi rapuh: setiap object harus membawa method-nya sendiri, dan tidak ada jaminan bentuk yang seragam. Class menjawab kebutuhan itu dengan cetak biru (blueprint): satu definisi yang bisa dipakai mencetak banyak instance seragam. Di Node.js, class kita temui di http.Server, stream, hingga class Error sendiri. Pada chapter ini kita akan menulis class dengan constructor dan method, melindungi data lewat getter dan setter, membangun hierarki dengan extends dan super, lalu menyelami prototype chain yang bekerja diam-diam di balik sintaks class.
Class, constructor, dan Method #
Kata kunci class mendefinisikan cetak biru; operator new mencetak instance baru darinya. Method khusus bernama constructor dijalankan sekali untuk setiap instance baru dan menjadi tempat menyiapkan data awal lewat this. Simpan berikut sebagai kelas-dasar.mjs lalu jalankan dengan node kelas-dasar.mjs:
class Tugas {
constructor(judul, durasiJam) {
this.judul = judul;
this.durasiJam = durasiJam;
this.selesai = false;
}
tandaiSelesai() {
this.selesai = true;
}
ringkas() {
const status = this.selesai ? "selesai" : "berjalan";
return `${this.judul} (${status}, ${this.durasiJam} jam)`;
}
}
const belajar = new Tugas("Belajar Node.js", 2);
const olahraga = new Tugas("Lari pagi", 1);
belajar.tandaiSelesai();
console.log(belajar.ringkas());
console.log(olahraga.ringkas());
console.log(`belajar instanceof Tugas : ${belajar instanceof Tugas}`);Output yang Dihasilkan:
Belajar Node.js (selesai, 2 jam)
Lari pagi (berjalan, 1 jam)
belajar instanceof Tugas : trueMari kita telaah baris-baris kode di atas:
- Baris 2-7 (
constructor): setiapnew Tugas(...)menjalankan constructor ini.thismenunjuk instance yang baru diciptakan; tiga property dipasang dengan nilai dari argumen, danselesaiselalu dimulai darifalse. - Baris 9-11 (
tandaiSelesai): method mengubah state instance. Method didefinisikan sekali di class, tetapi dipanggil melalui instance —belajar.tandaiSelesai()hanya mengubah instancebelajar, bukanolahraga. - Baris 13-17 (
ringkas): method membaca state terkini dan merangkainya menjadi string. Perhatikan bahwaolahragatetap"berjalan"karena hanyabelajaryang ditandai selesai. - Baris terakhir (
instanceof): operator ini menguji apakah sebuah instance diciptakan dari class tertentu — berguna untuk validasi sebelum memanggil method-nya.
Perlu dicatat bahwa dalam class, this pada method menunjuk instance pemanggil — aturan dynamic binding yang sama dengan method object biasa pada Chapter Function dan Arrow Function. Konsekuensinya juga sama: arrow function tidak cocok dipakai sebagai method class karena this-nya tidak menunjuk instance.
Getter dan Setter #
Getter dan setter membuat method terlihat seperti property biasa dari luar class. Manfaat utamanya adalah enkapsulasi: kode luar tetap menulis artikel.jumlahKata = 450 seperti memberi nilai property, sementara class menjalankan validasi di pintu masuk dan menyimpan nilai aslinya di property internal _jumlahKata. Berkas getter-setter.mjs memperlihatkan pola lengkapnya:
class Artikel {
constructor(judul) {
this.judul = judul;
this._jumlahKata = 0;
}
// getter: dibaca seperti property biasa
get jumlahKata() {
return this._jumlahKata;
}
// setter: jalankan validasi sebelum nilai disimpan
set jumlahKata(nilai) {
if (!Number.isInteger(nilai) || nilai < 0) {
throw new Error(`jumlah kata tidak valid: ${nilai}`);
}
this._jumlahKata = nilai;
}
// property terhitung dari data lain
get durasiBacaMenit() {
return Math.ceil(this._jumlahKata / 200);
}
}
const artikel = new Artikel("Panduan Node.js");
artikel.jumlahKata = 450;
console.log(`judul : ${artikel.judul}`);
console.log(`jumlah kata : ${artikel.jumlahKata}`);
console.log(`durasi baca : ${artikel.durasiBacaMenit} menit`);
try {
artikel.jumlahKata = -10;
} catch (e) {
console.log(`${e.name}: ${e.message}`);
}Output yang Dihasilkan:
judul : Panduan Node.js
jumlah kata : 450
durasi baca : 3 menit
Error: jumlah kata tidak valid: -10Alur eksekusinya:
- Baris 25 (
artikel.jumlahKata = 450): penugasan ini tidak menulis property secara langsung, melainkan memanggil setter. Validasi lolos karena450adalah bilangan bulat positif, lalu nilai disimpan ke_jumlahKata. - Baris 27 (
artikel.jumlahKata): pembacaan tanpa tanda sama dengan memanggil getter, yang mengembalikan nilai dari_jumlahKata. durasiBacaMenit: getter murni tanpa pasangan setter — property hitunganMath.ceil(450 / 200)yang selalu segar setiap kali dibaca, dengan asumsi kecepatan baca 200 kata per menit.- Baris 31-34: penugasan
-10ditolak setter dengan melemparError. Bloktry...catchmenangkapnya sehingga program tetap berjalan — teknik penanganan error yang akan kita bahas tuntas pada chapter berikutnya.
Inheritance dengan extends dan super #
Kata kunci extends membangun class anak yang mewarisi seluruh method class induk, dan super menjadi jembatan ke induk itu: super(...) memanggil constructor induk, super.method() memanggil method-nya. Method anak dengan nama yang sama menimpa (override) versi induk. Ada pula member static yang melekat pada class itu sendiri, bukan pada instance — tempat yang tepat untuk factory seperti buatRoot(). Semuanya terangkum dalam warisan-statis.mjs:
class Pengguna {
constructor(nama, email) {
this.nama = nama;
this.email = email;
}
perkenalan() {
return `${this.nama} (${this.email})`;
}
}
class Admin extends Pengguna {
constructor(nama, email, level) {
super(nama, email); // wajib sebelum memakai this
this.level = level;
}
perkenalan() {
return `[${this.level}] ${super.perkenalan()}`;
}
// method statis: melekat pada class, bukan pada instance
static buatRoot(nama) {
return new Admin(nama, `${nama.toLowerCase()}@example.com`, "root");
}
}
const admin = new Admin("Budi", "[email protected]", "staf");
const root = Admin.buatRoot("Siti");
console.log(admin.perkenalan());
console.log(root.perkenalan());
console.log(`admin instanceof Pengguna : ${admin instanceof Pengguna}`);Output yang Dihasilkan:
[staf] Budi ([email protected])
[root] Siti ([email protected])
admin instanceof Pengguna : trueBeberapa hal penting dari kode di atas:
super(nama, email)di constructorAdmin: class anak yang punya constructor sendiri wajib memanggilsuper()sebelum menyentuhthis— kalau tidak, eksekusi melemparReferenceError.perkenalan()versiAdminmenimpa versiPengguna, tetapi tetap bisa memanggil versi induk lewatsuper.perkenalan()lalu membungkusnya dengan label level.Admin.buatRoot("Siti"): method statis dipanggil langsung dari class tanpanew. Factory seperti ini menyembunyikan detail pembuatan (email dari nama, level"root") dari pemanggil.admin instanceof Penggunabernilaitrue: instance anak juga dianggap instance induk — dasar dari pola substitusi dalam object-oriented programming.
Prototype di Balik Class #
Sintaks class adalah lapisan manis di atas mekanisme yang lebih tua: prototype chain. Setiap class menyimpan method-methodnya di sebuah object bernama Pengguna.prototype (bisa diakses sebagai Class.prototype), dan setiap instance menyimpan tautan internal — dikenal sebagai __proto__ secara konseptual — ke object prototype milik class-nya. Saat kita menulis kucing.bersuara(), JavaScript mencari property bersuara pada instance lebih dulu; bila tidak ada, pencarian menyusuri rantai prototype. Perhatikan buktinya:
class Hewan {
bersuara() {
return "...";
}
}
const kucing = new Hewan();
console.log(`milik instance : ${Object.hasOwn(kucing, "bersuara")}`);
console.log(`tipe di prototype : ${typeof Hewan.prototype.bersuara}`);
console.log(`prototype kucing : ${Object.getPrototypeOf(kucing) === Hewan.prototype}`);
console.log(`induk prototype : ${Object.getPrototypeOf(Hewan.prototype) === Object.prototype}`);Output yang Dihasilkan:
milik instance : false
tipe di prototype : function
prototype kucing : true
induk prototype : trueBaris pertama membuktikan method tidak disalin ke setiap instance — bersuara bukan milik kucing sendiri. Baris kedua menunjukkan tempat sebenarnya: Hewan.prototype.bersuara adalah function. Dua baris terakhir memetakan rantainya: prototype kucing adalah Hewan.prototype, dan prototype dari Hewan.prototype adalah Object.prototype — ujung rantai tempat method seperti toString() tinggal. Inilah sebabnya instance Tugas kita bisa memanggil belajar.toString() meskipun kita tidak pernah menulis method itu.
Referensi lanjutan untuk topik chapter ini:
Class kini lengkap di tangan kita: constructor, getter/setter, warisan, sampai prototype yang menopangnya semua. Dalam contoh-contoh di atas, beberapa method dengan sengaja melempar Error ketika menerima masukan tidak valid — dan kita selalu menangkapnya dengan try...catch. Bab berikutnya membedah mekanisme itu secara menyeluruh: bentuk object Error, alur try-catch-finally, hingga cara membuat class error kustom. Lanjut ke Chapter Penanganan Error dengan try-catch.