Seperti yang kita ketahui, objek dapat menyimpan properti.
Hingga saat ini, bagi kami, properti hanyalah pasangan “nilai kunci” yang sederhana. Namun properti objek sebenarnya adalah hal yang lebih fleksibel dan kuat.
Dalam bab ini kita akan mempelajari opsi konfigurasi tambahan, dan pada bab berikutnya kita akan melihat cara mengubahnya menjadi fungsi pengambil/penyetel secara tidak terlihat.
Properti objek, selain value
, memiliki tiga atribut khusus (disebut “bendera”):
writable
– jika true
, nilainya dapat diubah, jika tidak, maka hanya dapat dibaca.
enumerable
– jika true
, maka dicantumkan dalam loop, jika tidak, tidak dicantumkan.
configurable
– jika true
, properti dapat dihapus dan atribut ini dapat diubah, jika tidak, tidak.
Kami belum melihatnya, karena umumnya mereka tidak muncul. Saat kita membuat properti “dengan cara biasa”, semuanya true
. Namun kami juga bisa mengubahnya kapan saja.
Pertama, mari kita lihat cara mendapatkan bendera tersebut.
Metode Object.getOwnPropertyDescriptor memungkinkan untuk menanyakan informasi lengkap tentang suatu properti.
Sintaksnya adalah:
biarkan deskriptor = Object.getOwnPropertyDescriptor(obj, propertyName);
obj
Objek untuk mendapatkan informasi.
propertyName
Nama properti.
Nilai yang dikembalikan disebut objek “deskriptor properti”: berisi nilai dan semua tanda.
Misalnya:
biarkan pengguna = { nama: "Yohanes" }; biarkan deskriptor = Object.getOwnPropertyDescriptor(pengguna, 'nama'); peringatan( JSON.stringify(deskriptor, null, 2 ) ); /* deskriptor properti: { "nilai": "John", "dapat ditulisi": benar, "enumerable": benar, "dapat dikonfigurasi": benar } */
Untuk mengubah flagnya, kita bisa menggunakan Object.defineProperty.
Sintaksnya adalah:
Objek.defineProperty(obj, propertyName, deskriptor)
obj
, propertyName
Objek dan propertinya untuk menerapkan deskriptor.
descriptor
Objek deskriptor properti yang akan diterapkan.
Jika properti ada, defineProperty
memperbarui tandanya. Jika tidak, ia akan menciptakan properti dengan nilai dan tanda tertentu; dalam hal ini, jika tanda tidak diberikan, maka dianggap false
.
Misalnya, di sini name
properti dibuat dengan semua tanda palsu:
biarkan pengguna = {}; Objek.defineProperty(pengguna, "nama", { nilai: "John" }); biarkan deskriptor = Object.getOwnPropertyDescriptor(pengguna, 'nama'); peringatan( JSON.stringify(deskriptor, null, 2 ) ); /* { "nilai": "John", "dapat ditulisi": salah, "enumerable": salah, "dapat dikonfigurasi": salah } */
Bandingkan dengan user.name
yang “dibuat secara normal” di atas: sekarang semua tanda salah. Jika bukan itu yang kita inginkan maka sebaiknya kita atur ke true
di descriptor
.
Sekarang mari kita lihat efek dari flag dengan contoh.
Mari kita buat user.name
tidak dapat ditulisi (tidak dapat ditetapkan ulang) dengan mengubah tanda writable
:
biarkan pengguna = { nama: "Yohanes" }; Objek.defineProperty(pengguna, "nama", { dapat ditulis: salah }); pengguna.nama = "Pete"; // Kesalahan: Tidak dapat menetapkan 'nama' properti hanya baca
Sekarang tidak ada yang bisa mengubah nama pengguna kami, kecuali mereka menerapkan defineProperty
mereka sendiri untuk mengganti nama kami.
Kesalahan hanya muncul dalam mode ketat
Dalam mode non-ketat, tidak ada kesalahan yang terjadi saat menulis ke properti yang tidak dapat ditulisi dan semacamnya. Namun operasinya tetap tidak berhasil. Tindakan yang melanggar bendera diabaikan begitu saja jika tidak ketat.
Berikut contoh yang sama, namun properti dibuat dari awal:
biarkan pengguna = {}; Objek.defineProperty(pengguna, "nama", { nilai: "John", // untuk properti baru kita perlu mencantumkan secara eksplisit apa yang benar dapat dihitung: benar, dapat dikonfigurasi: benar }); alert(nama pengguna); // Yohanes pengguna.nama = "Pete"; // Kesalahan
Sekarang mari tambahkan toString
khusus ke user
.
Biasanya, toString
bawaan untuk objek tidak dapat dihitung, tidak muncul di for..in
. Namun jika kita menambahkan toString
kita sendiri, maka secara default akan muncul di for..in
, seperti ini:
biarkan pengguna = { nama: "Yohanes", keString() { kembalikan nama ini; } }; // Secara default, kedua properti kami terdaftar: for (biarkan memasukkan pengguna) alert(key); // nama, toString
Jika kita tidak menyukainya, maka kita dapat mengatur enumerable:false
. Maka itu tidak akan muncul dalam loop for..in
, seperti yang ada di dalamnya:
biarkan pengguna = { nama: "Yohanes", keString() { kembalikan nama ini; } }; Objek.defineProperty(pengguna, "toString", { dapat dihitung: salah }); // Sekarang toString kita menghilang: for (biarkan memasukkan pengguna) alert(key); // nama
Properti yang tidak dapat dihitung juga dikecualikan dari Object.keys
:
alert(Object.keys(pengguna)); // nama
Bendera yang tidak dapat dikonfigurasi ( configurable:false
) terkadang telah diatur sebelumnya untuk objek dan properti bawaan.
Properti yang tidak dapat dikonfigurasi tidak dapat dihapus, atributnya tidak dapat diubah.
Misalnya, Math.PI
tidak dapat ditulis, tidak dapat dihitung, dan tidak dapat dikonfigurasi:
biarkan deskriptor = Object.getOwnPropertyDescriptor(Matematika, 'PI'); peringatan( JSON.stringify(deskriptor, null, 2 ) ); /* { "nilai": 3.141592653589793, "dapat ditulisi": salah, "enumerable": salah, "dapat dikonfigurasi": salah } */
Jadi, seorang programmer tidak dapat mengubah nilai Math.PI
atau menimpanya.
Matematika.PI = 3; // Error, karena dapat ditulisi: false // delete Math.PI juga tidak akan berhasil
Kami juga tidak dapat mengubah Math.PI
agar writable
lagi:
// Kesalahan, karena dapat dikonfigurasi: salah Object.defineProperty(Matematika, "PI", { dapat ditulis: benar });
Sama sekali tidak ada yang bisa kita lakukan dengan Math.PI
.
Membuat properti tidak dapat dikonfigurasi adalah jalan satu arah. Kami tidak dapat mengubahnya kembali dengan defineProperty
.
Harap dicatat: configurable: false
mencegah perubahan tanda properti dan penghapusannya, sekaligus memungkinkan untuk mengubah nilainya.
Di sini user.name
tidak dapat dikonfigurasi, tetapi kita masih dapat mengubahnya (karena dapat ditulis):
biarkan pengguna = { nama: "Yohanes" }; Objek.defineProperty(pengguna, "nama", { dapat dikonfigurasi: salah }); pengguna.nama = "Pete"; // berfungsi dengan baik hapus nama pengguna; // Kesalahan
Dan di sini kita menjadikan user.name
sebagai konstanta “yang tersegel selamanya”, sama seperti Math.PI
bawaan :
biarkan pengguna = { nama: "Yohanes" }; Objek.defineProperty(pengguna, "nama", { dapat ditulis: salah, dapat dikonfigurasi: salah }); // tidak akan bisa mengubah nama pengguna atau tandanya // semua ini tidak akan berhasil: pengguna.nama = "Pete"; hapus nama pengguna; Objek.defineProperty(pengguna, "nama", { nilai: "Pete" });
Satu-satunya perubahan atribut yang mungkin: dapat ditulis benar → salah
Ada sedikit pengecualian tentang perubahan bendera.
Kita dapat mengubah writable: true
menjadi false
untuk properti yang tidak dapat dikonfigurasi, sehingga mencegah modifikasi nilainya (untuk menambahkan lapisan perlindungan lain). Tapi bukan sebaliknya.
Ada metode Object.defineProperties(obj, descriptors) yang memungkinkan untuk mendefinisikan banyak properti sekaligus.
Sintaksnya adalah:
Objek.defineProperties(obj, { prop1: deskriptor1, prop2: deskriptor2 // ... });
Misalnya:
Objek.defineProperties(pengguna, { nama: { nilai: "John", dapat ditulis: salah }, nama keluarga: { nilai: "Smith", dapat ditulis: false }, // ... });
Jadi, kita bisa mengatur banyak properti sekaligus.
Untuk mendapatkan semua deskriptor properti sekaligus, kita bisa menggunakan metode Object.getOwnPropertyDescriptors(obj).
Bersama dengan Object.defineProperties
ini dapat digunakan sebagai cara "sadar bendera" untuk mengkloning suatu objek:
biarkan clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));
Biasanya saat kita mengkloning suatu objek, kita menggunakan tugas untuk menyalin properti, seperti ini:
untuk (biarkan memasukkan pengguna) { clone[kunci] = pengguna[kunci] }
…Tapi itu tidak meniru bendera. Jadi jika kita menginginkan klon yang “lebih baik” maka Object.defineProperties
lebih disukai.
Perbedaan lainnya adalah for..in
mengabaikan properti simbolik dan non-enumerable, tetapi Object.getOwnPropertyDescriptors
mengembalikan semua deskriptor properti termasuk yang simbolis dan non-enumerable.
Deskriptor properti bekerja pada tingkat properti individual.
Ada juga metode yang membatasi akses ke seluruh objek:
Objek.preventExtensions(obj)
Melarang penambahan properti baru pada objek.
Objek.segel(obj)
Melarang penambahan/penghapusan properti. Set configurable: false
untuk semua properti yang ada.
Objek.freeze(obj)
Melarang menambah/menghapus/mengubah properti. Menyetel configurable: false, writable: false
untuk semua properti yang ada.
Dan juga ada ujian bagi mereka:
Objek.isExtensible(obj)
Mengembalikan false
jika penambahan properti dilarang, jika tidak, true
.
Objek.isSealed(obj)
Mengembalikan true
jika menambahkan/menghapus properti dilarang, dan semua properti yang ada dapat configurable: false
.
Objek.isFrozen(obj)
Mengembalikan true
jika menambahkan/menghapus/mengubah properti dilarang, dan semua properti saat ini configurable: false, writable: false
.
Metode-metode ini jarang digunakan dalam praktik.