Bab 6: Konfigurasi Properti Objek
6.1 Property Flags & Descriptor
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
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
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)
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)
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: falseitu jalan satu arah. Sekali diset, tidak bisa dikembalikan!
Membuat Properti "Terkunci Selamanya"
Object.defineProperty(user, "name", {
writable: false,
configurable: false
});
// Sekarang name tidak bisa diubah, dihapus, atau dimodifikasi flag-nyadefineProperties (Banyak Sekaligus)
Object.defineProperties(user, {
name: { value: "Budi", writable: false },
age: { value: 25, writable: false }
});Mengunci Seluruh Objek
| Method | Efek |
|---|---|
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 |
const config = { apiUrl: "https://api.example.com" };
Object.freeze(config);
config.apiUrl = "hacked!"; // Gagal
config.newProp = "test"; // GagaldefinePropertytanpa flag = semua false! Kalau kamu bikin properti baru pakaidefinePropertytanpa menyebutkan flag, semua flag default-nyafalse(bukantrueseperti biasa).freezeitu shallow. Kalau properti berisi objek lain, isi objek dalamnya tetap bisa diubah.
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
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
- Data property — punya
value(yang biasa kita pakai) - Accessor property — punya
getdanset(fungsi yang jalan otomatis)
Contoh Dasar
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 aksesfullName. Dari luar, dia terlihat seperti properti biasa!
Getter/Setter dengan defineProperty
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 birthdaySmart Setter (Validasi)
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:
// 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!-
Tidak bisa campur
valuedanget/set! Satu properti hanya boleh salah satu: data property ATAU accessor property.js// ERROR! Object.defineProperty({}, "x", { value: 1, get() { return 2; } }); -
Getter tanpa setter = read-only. Kalau cuma bikin
gettanpaset, assign ke properti itu akan error (strict mode) atau diam-diam gagal. -
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; }
Buat objek temperature yang:
- Punya properti
celsius(bisa di-set) - Punya getter
fahrenheityang otomatis menghitung dari celsius (rumus:celsius * 9/5 + 32) - Punya setter
fahrenheityang mengkonversi balik ke celsius
// Harusnya bisa:
temperature.celsius = 100;
console.log(temperature.fahrenheit); // 212
temperature.fahrenheit = 32;
console.log(temperature.celsius); // 0Sudah paham materi ini?
Tandai sebagai selesai untuk melacak progress-mu.