Bab 6: Konfigurasi Properti Objek

3 menit baca

6.1 Property Flags & Descriptor

💡Analogi

Bayangin properti objek itu kayak barang di etalase toko. Selain harga (value), setiap barang punya aturan:

  • Boleh diganti harganya? (writable)
  • Boleh ditampilkan di katalog? (enumerable)
  • Boleh dihapus atau diubah aturannya? (configurable)

Secara default, semua aturan ini true kalau kamu bikin properti biasa. Tapi kamu bisa mengatur ulang.

Melihat Descriptor

js
const user = { name: "Budi" };

const desc = Object.getOwnPropertyDescriptor(user, "name");
console.log(desc);
// {
//   value: "Budi",
//   writable: true,
//   enumerable: true,
//   configurable: true
// }

Mengubah Flag dengan defineProperty

js
const user = { name: "Budi" };

// Bikin name jadi read-only
Object.defineProperty(user, "name", {
  writable: false
});

user.name = "Andi"; // Diam-diam gagal (strict mode: Error!)
console.log(user.name); // "Budi"

Non-enumerable (Tidak Muncul di Loop)

js
const user = {
  name: "Budi",
  toString() {
    return this.name;
  }
};

// Sembunyikan toString dari for..in
Object.defineProperty(user, "toString", {
  enumerable: false
});

for (let key in user) {
  console.log(key); // Hanya "name"
}

Non-configurable (Tidak Bisa Diubah Lagi)

js
const user = { name: "Budi" };

Object.defineProperty(user, "name", {
  configurable: false
});

// Sekarang tidak bisa:
delete user.name; // Error!
Object.defineProperty(user, "name", { enumerable: false }); // Error!

Penting: configurable: false itu jalan satu arah. Sekali diset, tidak bisa dikembalikan!

Membuat Properti "Terkunci Selamanya"

js
Object.defineProperty(user, "name", {
  writable: false,
  configurable: false
});
// Sekarang name tidak bisa diubah, dihapus, atau dimodifikasi flag-nya

defineProperties (Banyak Sekaligus)

js
Object.defineProperties(user, {
  name: { value: "Budi", writable: false },
  age: { value: 25, writable: false }
});

Mengunci Seluruh Objek

MethodEfek
Object.preventExtensions(obj)Tidak bisa tambah properti baru
Object.seal(obj)Tidak bisa tambah/hapus properti
Object.freeze(obj)Tidak bisa tambah/hapus/ubah properti
js
const config = { apiUrl: "https://api.example.com" };
Object.freeze(config);

config.apiUrl = "hacked!"; // Gagal
config.newProp = "test";   // Gagal
⚠️Jebakan!
  1. defineProperty tanpa flag = semua false! Kalau kamu bikin properti baru pakai defineProperty tanpa menyebutkan flag, semua flag default-nya false (bukan true seperti biasa).
  2. freeze itu shallow. Kalau properti berisi objek lain, isi objek dalamnya tetap bisa diubah.
🎯Challenge

Buat objek settings dengan properti theme yang:

  • Nilainya "dark"
  • Tidak bisa diubah nilainya
  • Tidak bisa dihapus
  • Tapi tetap muncul di for..in

6.2 Getter dan Setter

💡Analogi

Bayangin kamu punya KTP. Di KTP ada field "Umur", tapi umur itu bukan disimpan langsung — dia dihitung dari tanggal lahir. Getter itu kayak field yang nilainya dihitung otomatis saat kamu baca. Setter itu kayak form yang memproses input sebelum disimpan.

Dua Jenis Properti

  1. Data property — punya value (yang biasa kita pakai)
  2. Accessor property — punya get dan set (fungsi yang jalan otomatis)

Contoh Dasar

js
const user = {
  firstName: "Budi",
  lastName: "Santoso",

  // Getter: dibaca kayak properti biasa
  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  },

  // Setter: di-assign kayak properti biasa
  set fullName(value) {
    [this.firstName, this.lastName] = value.split(" ");
  }
};

console.log(user.fullName); // "Budi Santoso" (getter jalan)

user.fullName = "Andi Wijaya"; // setter jalan
console.log(user.firstName);  // "Andi"
console.log(user.lastName);   // "Wijaya"

Perhatikan: kita tidak pakai () saat akses fullName. Dari luar, dia terlihat seperti properti biasa!

Getter/Setter dengan defineProperty

js
const user = {
  name: "Budi",
  birthday: new Date(1995, 5, 15)
};

Object.defineProperty(user, "age", {
  get() {
    return new Date().getFullYear() - this.birthday.getFullYear();
  }
});

console.log(user.age); // Dihitung otomatis dari birthday

Smart Setter (Validasi)

js
const user = {
  get name() {
    return this._name;
  },

  set name(value) {
    if (value.length < 3) {
      console.log("Nama terlalu pendek! Minimal 3 karakter.");
      return;
    }
    this._name = value;
  }
};

user.name = "Bu"; // "Nama terlalu pendek!"
user.name = "Budi"; // OK
console.log(user.name); // "Budi"

Konvensi _: Properti yang diawali underscore (_name) artinya "internal, jangan diakses langsung dari luar". Ini bukan aturan bahasa, tapi kesepakatan programmer.

Kegunaan: Kompatibilitas

Getter/setter berguna saat kamu ingin mengubah cara data disimpan tanpa merusak kode lama:

js
// Dulu: simpan age langsung
// Sekarang: simpan birthday, tapi age tetap bisa diakses

function User(name, birthday) {
  this.name = name;
  this.birthday = birthday;

  Object.defineProperty(this, "age", {
    get() {
      return new Date().getFullYear() - this.birthday.getFullYear();
    }
  });
}

const budi = new User("Budi", new Date(1995, 5, 15));
console.log(budi.age); // Tetap works!
⚠️Jebakan!
  1. Tidak bisa campur value dan get/set! Satu properti hanya boleh salah satu: data property ATAU accessor property.

    js
    // ERROR!
    Object.defineProperty({}, "x", {
      value: 1,
      get() { return 2; }
    });
  2. Getter tanpa setter = read-only. Kalau cuma bikin get tanpa set, assign ke properti itu akan error (strict mode) atau diam-diam gagal.

  3. Infinite loop! Hati-hati jangan set properti yang sama di dalam setter-nya sendiri:

    js
    // SALAH - infinite loop!
    set name(value) {
      this.name = value; // Memanggil setter lagi!
    }
    
    // BENAR - simpan di properti berbeda
    set name(value) {
      this._name = value;
    }
🎯Challenge

Buat objek temperature yang:

  • Punya properti celsius (bisa di-set)
  • Punya getter fahrenheit yang otomatis menghitung dari celsius (rumus: celsius * 9/5 + 32)
  • Punya setter fahrenheit yang mengkonversi balik ke celsius
js
// Harusnya bisa:
temperature.celsius = 100;
console.log(temperature.fahrenheit); // 212

temperature.fahrenheit = 32;
console.log(temperature.celsius); // 0

Sudah paham materi ini?

Tandai sebagai selesai untuk melacak progress-mu.