Seperti yang kita ketahui, objek dapat menyimpan banyak properti.
Sampai sekarang, sebuah properti hanyalah pasangan nilai dan kunci bagi kita. Tapi, sebuah properti objek sebenarnya lebih fleksibel dan memiliki banyak kegunaan.
Pada bab ini kita akan mempelajari konfigurasi tambahan dan pada bab selanjutnya kita akan melihat bagaimana secara samar mengubahnya menjadi fungsi getter atau setter.
Properti Flag
Properti Objek, selain sebuah Nilai
, memiliki tiga atribut spesial (yang dinamakan ???flags???):
writable
??? jikabenar
, maka nilai nya bisa diubah, jika tidak maka hanya bsa dibaca.enumerable
??? jikabenar
, maka akan dicantumkan pada daftar perulangan, jika tidak maka tidak akan dicantumkan. (Menentukan apakah bisa melakukan perulangan dengan properti objek tersebut)configurable
??? jikabenar
, properti itu dapat dihapus dan attribute-attributenya bisa diubah-ubah, jika tidak maka tidak bisa dihapus dan diubah.
catatan: benar
disini biasanya di gambarkan sebagai boolean true
dan salah
digambarkan sebagai boolean false
pada javascript.
Kita belum melihat mereka, karena biasanya mereka tidak muncul. Ketika kita membuat sebuah properti ???dengan cara biasa???, Semua dari tiga attribut diatas biasanya bernilai benar
.namun, kita juga bisa mengubahnya kapan pun kita mau.
Pertama, mari kita lihat bagaimana cara mendapatkan properti flag tersebut.
Pada Method Object.getOwnPropertyDescriptor memperbolehkan kita untuk melakukan query terhadap informasi komplit dari sebuah properti.
Sintaksnya adalah:
let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName);
-
obj
- Objek yang akan kita ambil informasinya.
-
propertyName
- Nama dari properti tersebut.
Nilai yang akan dikembalikan disebut sebagai ???properti deskriptor??? dari objek: didalamnya mengandung nilai dan semua flag dari properti tersebut.
Sebagai contoh:
let user = {
name: "John"
};
let descriptor = Object.getOwnPropertyDescriptor(user, 'name');
alert( JSON.stringify(descriptor, null, 2 ) );
/* property descriptor:
{
"Nilai": "John",
"writable": true,
"enumerable": true,
"configurable": true
}
*/
Untuk mengganti flag tersebut, kita dapat menggunakan Object.defineProperty.
Sintaksnya adalah:
Object.defineProperty(obj, propertyName, descriptor)
-
obj
,propertyName
- Objek dan nama properti yang akan diterapkan deskriptornya.
-
descriptor
- Properti deskriptor objek yang akan digunakan.
Jika properti tersebut ada, defineProperty
akan memperbarui flagnya. namun, itu akan membuat properti dengan nilai yang diberikan dan flag properti tersebut; pada kasus itu, jika sebuah flag tidak disediakan, maka itu akan diasumsikan dengan nilai false
(salah).
Sebagai contoh, sebuah properti name
dibuat dengan semua nilai flag yang salah:
let user = {};
Object.defineProperty(user, "name", {
value: "John"
});
let descriptor = Object.getOwnPropertyDescriptor(user, 'name');
alert( JSON.stringify(descriptor, null, 2 ) );
/*
{
"value": "John",
"writable": false,
"enumerable": false,
"configurable": false
}
*/
bandingkan dengan ???pembuatan cara biasa??? user.name
diatas: sekarang semua nilai flagnya berbentuk salah. Jika itu yang bukan kita mau maka kita sebaiknya merubahnya menjadi true
pada deskriptor
nya.
Sekarang mari kita lihat efek flag tersebut dengan contoh.
Non-writable
Ayo kita buat user.name
menjadi non-writable (tidak bisa diatur dan ditetapkan kembali) dengan mengubah flag writable
nya :
let user = {
name: "John"
};
Object.defineProperty(user, "name", {
writable: false
});
user.name = "Pete"; // Error: Cannot assign to read only property 'name'
Sekarang tidak ada yang dapat mengubah nama dari user kita, kecuali mereka menetapkan defineProperty
nya sendiri untuk menimpa konfigurasi kita.
Pada mode non-strict, tidak ada eror yang terjadi ketika menulis pada properti non-writable dan sejenisnya. tapi operasi itu tetap tidak akan berhasil. Aksi pelanggaran flag hanya saja diabaikan pada mode non-strict.
Berikut contoh kasus yang sama, tapi properti itu dibuat dari awal:
let user = { };
Object.defineProperty(user, "name", {
value: "John",
// untuk properti baru kita butuh untuk memberi tau apa saja yang benar/true
enumerable: true,
configurable: true
});
alert(user.name); // John
user.name = "Pete"; // Error
Non-enumerable
Sekarang mari kita tambahkan toString
yang sudah disesuaikan pada user
.
Secara normal, bawaan toString
untuk objek merupakan non-enumerable (tidak bisa diiterasi), itu tidak akan muncul pada for..in
. Tapi apabila kita tambahkantoString
yang kita buat sendiri, maka secara default akan muncul pada for..in
, contohnya seperti ini:
let user = {
name: "John",
toString() {
return this.name;
}
};
// secara default, kedua properti tersebut akan terdaftar:
for (let key in user) alert(key); // name, toString
Jika kita tidak menyukainya, maka kita bisa mengatur menjadi enumerable:false
. yang kemudian itu tidak akan tampil pada for..in
loop, seperti pada bawaannya:
let user = {
name: "John",
toString() {
return this.name;
}
};
Object.defineProperty(user, "toString", {
enumerable: false
});
// sekarang toString kita tidak akan muncul:
for (let key in user) alert(key); // name
Properti non-enumerable juga tidak termasuk dari Object.keys
:
alert(Object.keys(user)); // name
Non-configurable
Flag non-configurable (configurable:false
) terkadang sudah diatur sebelumnya untuk objek dan properti bawaan.
Sebuah properti non-configurable tidak bisa di hapus.
Sebagai contoh, Math.PI
adalah non-writable, non-enumerable and non-configurable:
let descriptor = Object.getOwnPropertyDescriptor(Math, 'PI');
alert( JSON.stringify(descriptor, null, 2 ) );
/*
{
"value": 3.141592653589793,
"writable": false,
"enumerable": false,
"configurable": false
}
*/
Jadi, seorang programer tidak akan bisa mengganti nilai dari sebuah Math.PI
atau juga menimpanya.
Math.PI = 3; // Error
// menghapus Math.PI juga tidak akan bekerja
Membuat sebuah properti non-configurable adalah jalan satu arah. kita tidak bisa mengubahnya kembali dengan defineProperty
.
Tepatnya, non-configurable memberlakukan beberapa pembatasan pada defineProperty
:
- tidak bisa mengubah flag
configurable
. - tidak bisa mengubah flag
enumerable
. - tidak bisa mengubah
writable: false
menjaditrue
(kebalikannya masih bisa bekerja). - tidak bisa mengubah
get/set
untuk sebuah properti aksesor (tapi bisa menetapkannya jika kosong).
Disini kita membuat user.name
menjadi sebuah konstant ???yang selamanya tersegel???:
let user = {
name: "John"
};
Object.defineProperty(user, "name", {
configurable: false
});
user.name = "Pete"; // works fine
delete user.name; // Error
And here we make user.name
a ???forever sealed??? constant:
let user = {
name: "John"
};
Object.defineProperty(user, "name", {
writable: false,
configurable: false
});
// tidak bisa mengubah user.name atau flag nya
// semua dibawah ini tidak akan bekerja:
// user.name = "Pete"
// delete user.name
// Object.defineProperty(user, "name", { value: "Pete" })
Object.defineProperty(user, "name", {writable: true}); // Error
Catatan pengecualian: sebuah nilai dari non-configurable, tapi writable properti masih bisa diubah.
Ide dari configurable: false
adalah untuk mencegah perubahan properti flag dan penghapusannya, bukan perubahan dalam nilainya.
Object.defineProperties
Ada sebuah method Object.defineProperties(obj, descriptors) yang memperbolehkan untuk mendefinisikan banyak properti pada satu waktu.
Sintaksnya adalah:
Object.defineProperties(obj, {
prop1: descriptor1,
prop2: descriptor2
// ...
});
Sebagai contoh:
Object.defineProperties(user, {
name: { value: "John", writable: false },
surname: { value: "Smith", writable: false },
// ...
});
Jadi, kita bisa mengatur banyak properti dalam satu waktu.
Object.getOwnPropertyDescriptors
Untuk mendapat semua properti deskriptor pada satu waktu, kita dapat menggunakan sebuah method Object.getOwnPropertyDescriptors(obj).
Bersamaan dengan Object.defineProperties
itu dapat digunakan menjadi cara ???flags-aware??? untuk mengkloning sebuah objek:
let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));
Normalnya ketika kita mengkloning sebuah objek, kita menggunakan penetapan nilai untuk menyalin propertinya, seperti ini:
for (let key in user) {
clone[key] = user[key]
}
???Tapi itu tidak menyalin flagnya. jadi jika kita ingin sesuatu salinan ???yang lebih baik??? maka Object.defineProperties
lebih disarankan.
Perbedaan lainnya adalah jika for..in
mengabaikan properti simbolik, tapi Object.getOwnPropertyDescriptors
mengembalikan semua properti deskriptor termasuk properti simboliknya.
menyegel sebuah objek secara global
Properti deskriptor bekerja pada level invidual propertinya.
Dan ada juga method yang memberi batasan akses terkait keselurhan objek:
- Object.preventExtensions(obj)
- Melarang penambahan pada properti baru dalam objek.
- Object.seal(obj)
-
Melarang penambahan/penghapusan dari properti. Menetapkan
configurable: false
untuk semua properti yang ada. - Object.freeze(obj)
-
Melarang penambahan/pengurangan/pengubahan pada properti. Menetapkan
configurable: false, writable: false
untuk semua properti yang ada.
Dan ada juga test untuk mereka:
- Object.isExtensible(obj)
-
Mengembalikan
false
jika menambahkan properti itu dilarang, selain itutrue
. - Object.isSealed(obj)
-
Mengembalikan
true
jika menambahkan/mengurangi properti itu dilarang, dan semua properti yang ada memilikiconfigurable: false
. - Object.isFrozen(obj)
-
Mengembalikan
true
jika menambahkan/mengurangi/mengubah properti itu dilarang, dan semua properti yang sekarang memilikiconfigurable: false, writable: false
.
Method diatas biasanya jarang digunakan pada prakteknya.