Satu email yang buruk mampu merusak banyak laporan. Di kafe Budi, data anggota pernah terisi " [email protected] " lengkap dengan spasi dan huruf besar tak konsisten — lalu filter pengiriman yang membandingkan email gagal menemukan penerima yang jelas-jelas terdaftar. Membersihkan data setelahnya berarti menyapu semua pemakai; jauh lebih bijak memastikan setiap nilai yang masuk sudah bersih sejak ditulis.

Untuk kebutuhan semacam ini, class menyediakan sintaks get dan set: properti yang tampak biasa dari luar, tetapi menjalankan function saat dibaca dan saat ditulis. Chapter ini memasang pasangan getter-setter pada class ProfilMember, menjalankan validasi di setter, dan menunjukkan perilaku getter yang sengaja dibiarkan tanpa setter.

get dan set: Properti Berperilaku function #

Aturannya: method di badan class yang diawali get menjadi pembaca sebuah properti, dan yang diawali set menjadi penulisnya. Dari luar, keduanya dipakai seperti properti biasa — tanpa tanda kurung, tanpa nama method — meski di baliknya function benar-benar berjalan.

Mekanismenya dinamai accessor property. Properti biasa menyimpan nilai langsung; properti accessor tidak menyimpan nilai, melainkan dua function yang dipanggil engine pada momen yang tepat: get saat properti dibaca, set saat properti diberi nilai. Karena itu sisi luar tidak pernah tahu — dan tidak perlu tahu — bahwa di balik profil.email ada pembersihan spasi, penyeragaman huruf, dan pemeriksaan format.

Pasangan ini juga menjawab masalah baca-tulis pada chapter private fields. Field #email terkunci dari luar, tetapi getter email membukanya untuk dibaca, dan setter email membukanya untuk ditulis dengan syarat. Class memegang kendali penuh di dua titik itu.

Validasi di Setter: Menolak Data Buruk #

Berkas berikut menyatukan semua konsep: field privat, setter yang menormalisasi lalu memvalidasi, dan getter tanpa pasangan:

// profil-member.js
// get dan set: properti yang dihitung dan divalidasi.

"use strict";

class ProfilMember {
  #email;

  constructor(nama, email) {
    this.nama = nama;
    this.email = email;
  }

  get email() {
    return this.#email;
  }

  set email(nilaiBaru) {
    const bersih = nilaiBaru.trim().toLowerCase();
    if (!bersih.includes("@")) {
      throw new TypeError(`Alamat email tidak valid: ${nilaiBaru}`);
    }
    this.#email = bersih;
  }

  get sebutan() {
    return `Member ${this.nama}`;
  }
}

const profilSiti = new ProfilMember("Siti", "  [email protected] ");

console.log(profilSiti.email);
console.log(profilSiti.sebutan);

try {
  profilSiti.email = "siti-tanpa-alamat";
} catch (err) {
  console.log(`${err.name}: ${err.message}`);
}

profilSiti.email = "[email protected]";
console.log(profilSiti.email);

try {
  profilSiti.sebutan = "Bukan member";
} catch (err) {
  console.log(`${err.name}: ${err.message}`);
}

Output yang Dihasilkan:

text
[email protected]
Member Siti
TypeError: Alamat email tidak valid: siti-tanpa-alamat
[email protected]
TypeError: Cannot set property sebutan of #<ProfilMember> which has only a getter

Mari kita bedah:

  1. Baris 11 (this.email = email;): penugasan di constructor memanggil setter — data punya #email sejak instance lahir, dan sudah melewati pembersihan yang sama seperti penugasan di luar. Tidak ada jalur tulis yang memintas validasi.
  2. Baris 14–16 (get email): pembacaan mengembalikan isi field privat apa adanya — nilai yang dijamin sudah bersih.
  3. Baris 18–24 (set email): nilai baru dipangkas dan diseragamkan huruf kecilnya lebih dulu; bila tidak memuat @, setter melempar TypeError dan penugasan gagal — field lama tetap utuh.
  4. Baris 26–28 (get sebutan): getter yang menghitung nilai dari data lain saat dibaca, tanpa penyimpanan apa pun — inilah properti terhitung (computed property).
  5. Baris 31 (new ProfilMember("Siti", " [email protected] ")): input kotor dari form; hasilnya tersimpan sebagai [email protected] tanpa satu pun penanganan di sisi pemanggil.
  6. Baris 36–40 (penulisan tanpa @): TypeError tertangkap try...catch; pesannya menyebut nilai yang ditolak, dan email Siti tidak berubah.
  7. Baris 42–43 (penulisan sah): email baru diterima setter, dibersihkan, dan langsung terbaca dari getter.
  8. Baris 45–48 (menulis ke getter tanpa setter): sebutan hanya punya get; penugasan kepadanya melempar TypeError yang tercetak di dua baris terakhir keluaran.

Getter Saja Berarti Hanya Baca #

Dua baris terakhir keluaran membuktikan aturan yang penting: properti accessor yang hanya memiliki get bersifat hanya baca. Bila file dijalankan tanpa "use strict", penugasan ke sebutan tidak menimbulkan galat apa pun — penulisannya gagal senyap dan nilai lama tetap terpakai. Karena modul dan badan class selalu berjalan ketat, gagal senyap semacam itu jarang dijumpai di kode modern; tetap baik kita tahu perilaku keduanya.

Getter terhitung seperti sebutan juga menjaga konsistensi otomatis. Bila nama diperbarui, sebutan langsung mengikuti karena nilainya tidak pernah disimpan — ia dihitung ulang setiap dibaca. Perbandingan singkatnya:

BentukDisimpan?Contoh pas
Properti biasa namaYa, di instanceData pokok yang berubah-ubah
Getter terhitung sebutanTidak, dihitung saat dibacaTurunan dari data lain: sebutan, total, label
Setter dengan validasiYa, lewat penjagaanData dengan aturan: email, poin, tanggal

Sifat “dihitung ulang saat dibaca” itu bisa dibuktikan dengan mengubah data pokoknya. Getter yang menghitung tidak pernah usang, karena ia tidak menyimpan apa pun:

js
class ProfilMember {
  constructor(nama) {
    this.nama = nama;
  }

  get sebutan() {
    return `Member ${this.nama}`;
  }
}

const profil = new ProfilMember("Siti");
console.log(profil.sebutan);
profil.nama = "Siti Rahma";
console.log(profil.sebutan);

Output yang Dihasilkan:

text
Member Siti
Member Siti Rahma

Setelah nama diperbarui, sebutan langsung mengikuti tanpa ada yang menyinkronkannya — kebalikan dari menyimpan salinan "Member Siti" yang berisiko tertinggal ketika data aslinya berubah.

Dua Gaya Setter: Melempar atau Menyelaraskan #

Setter email memilih gaya keras: nilai yang tidak sah ditolak dengan throw. Gaya kedua sama sahnya — menyelaraskan nilai ke rentang yang diizinkan tanpa mengganggu pemanggil. Pilihan yang tepat mengikuti makna datanya: email yang salah tidak punya “nilai terdekat yang wajar”, sedangkan tingkat kepekatan kopi punya rentang 1–5 yang bisa ditekuk:

js
class SetelanKopi {
  #level = 1;

  set level(nilai) {
    const angka = Number(nilai);
    this.#level = angka >= 1 && angka <= 5 ? angka : 1;
  }

  get level() {
    return this.#level;
  }
}

const setelan = new SetelanKopi();
setelan.level = "4";
console.log(setelan.level);
setelan.level = 99;
console.log(setelan.level);

Output yang Dihasilkan:

text
4
1

Setter menyelaraskan dua hal sekaligus: teks "4" dikonversi menjadi angka 4, dan 99 yang di luar rentang jatuh ke nilai cadangan 1. Pemanggil tidak pernah melihat galat, tetapi juga tidak pernah berhasil menyimpan nilai liar. Aturan praktisnya singkat: data identitas — email, nomor rekening — layak ditolak; data preferensi yang punya rentang — kepekatan, volume, ukuran — cukup diselaraskan.

get dan set di Object Literal #

Sintaks accessor ini bukan milik class saja — object literal juga menerimanya, sejak jauh sebelum class ada. Bentuknya persis sama:

js
const konfigurasiKafe = {
  nama: "Kafe Senja",
  get label() {
    return `Selamat datang di ${this.nama}`;
  },
};
console.log(konfigurasiKafe.label);

Output yang Dihasilkan:

text
Selamat datang di Kafe Senja

Getter label membaca this.nama saat diakses, sehingga perubahan nama otomatis tercermin pada label. Karena setter dan getter sudah kita kuasai di kedua tempat ini, pilihan tinggal soal selera dan kebutuhan: class untuk cetakan banyak instance, object literal untuk satu konfigurasi tunggal.

Sampai di sini, setiap class kita tumbuh menjadi satu unit yang kokoh: data privat, constructor, static, dan accessor. Chapter terakhir section ini melangkah keluar dari perbandingan sintaks dan menimbang keputusan desain yang sesungguhnya — kapan sebuah kemampuan lebih baik diwariskan lewat extends, dan kapan lebih baik disusun sebagai bagian terpisah yang dipasangkan.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

profil-member.js

Unduh berkas
javascript
// profil-member.js
// get dan set: properti yang dihitung dan divalidasi.

"use strict";

class ProfilMember {
  #email;

  constructor(nama, email) {
    this.nama = nama;
    this.email = email;
  }

  get email() {
    return this.#email;
  }

  set email(nilaiBaru) {
    const bersih = nilaiBaru.trim().toLowerCase();
    if (!bersih.includes("@")) {
      throw new TypeError(`Alamat email tidak valid: ${nilaiBaru}`);
    }
    this.#email = bersih;
  }

  get sebutan() {
    return `Member ${this.nama}`;
  }
}

const profilSiti = new ProfilMember("Siti", "  [email protected] ");

console.log(profilSiti.email);
console.log(profilSiti.sebutan);

try {
  profilSiti.email = "siti-tanpa-alamat";
} catch (err) {
  console.log(`${err.name}: ${err.message}`);
}

profilSiti.email = "[email protected]";
console.log(profilSiti.email);

try {
  profilSiti.sebutan = "Bukan member";
} catch (err) {
  console.log(`${err.name}: ${err.message}`);
}