Ada dua jenis properti objek.
Jenis pertama adalah properti data . Kami sudah tahu cara bekerja dengan mereka. Semua properti yang kami gunakan hingga saat ini adalah properti data.
Jenis properti yang kedua adalah sesuatu yang baru. Ini adalah properti pengakses . Mereka pada dasarnya adalah fungsi yang dijalankan untuk mendapatkan dan menetapkan nilai, tetapi terlihat seperti properti biasa bagi kode eksternal.
Properti pengakses diwakili oleh metode “pengambil” dan “penyetel”. Dalam literal objek, mereka dilambangkan dengan get
dan set
:
misalkan obj = { dapatkan propName() { // pengambil, kode yang dieksekusi saat mendapatkan obj.propName }, setel nama prop(nilai) { // setter, kode dieksekusi pada setting obj.propName = value } };
Pengambil berfungsi ketika obj.propName
dibaca, penyetel – ketika ditugaskan.
Misalnya, kita memiliki objek user
dengan name
dan surname
:
biarkan pengguna = { nama: "Yohanes", nama keluarga: "Smith" };
Sekarang kami ingin menambahkan properti fullName
, yaitu "John Smith"
. Tentu saja kita tidak ingin melakukan copy-paste informasi yang ada, sehingga kita dapat mengimplementasikannya sebagai pengakses:
biarkan pengguna = { nama: "Yohanes", nama keluarga: "Smith", dapatkan Nama Lengkap() { return `${nama ini} ${nama belakang ini}`; } }; alert(pengguna.Nama Lengkap); // John Smith
Dari luar, properti pengakses tampak seperti properti biasa. Itulah gagasan tentang properti pengakses. Kami tidak memanggil user.fullName
sebagai fungsi, kami membacanya secara normal: pengambil berjalan di belakang layar.
Sampai sekarang, fullName
hanya memiliki pengambil. Jika kami mencoba menetapkan user.fullName=
, akan ada kesalahan:
biarkan pengguna = { dapatkan Nama Lengkap() { kembali `...`; } }; pengguna.nama lengkap = "Tes"; // Error (properti hanya memiliki pengambil)
Mari kita perbaiki dengan menambahkan penyetel untuk user.fullName
:
biarkan pengguna = { nama: "Yohanes", nama keluarga: "Smith", dapatkan Nama Lengkap() { return `${nama ini} ${nama belakang ini}`; }, setel Nama Lengkap(nilai) { [nama.ini, nama belakang ini] = nilai.split(" "); } }; // set fullName dijalankan dengan nilai yang diberikan. pengguna.nama lengkap = "Alice Cooper"; alert(nama pengguna); // Alice alert(pengguna.nama belakang); // Kerjasama
Hasilnya, kita memiliki properti “virtual” fullName
. Itu dapat dibaca dan ditulis.
Deskriptor untuk properti pengakses berbeda dengan deskriptor untuk properti data.
Untuk properti pengakses, tidak ada value
atau writable
, melainkan ada fungsi get
dan set
.
Artinya, deskriptor pengakses mungkin memiliki:
get
– fungsi tanpa argumen, yang berfungsi saat properti dibaca,
set
– fungsi dengan satu argumen, yang dipanggil ketika properti disetel,
enumerable
– sama seperti untuk properti data,
configurable
– sama seperti untuk properti data.
Misalnya, untuk membuat pengakses fullName
dengan defineProperty
, kita dapat meneruskan deskriptor dengan get
dan set
:
biarkan pengguna = { nama: "Yohanes", nama keluarga: "Smith" }; Objek.defineProperty(pengguna, 'Nama Lengkap', { mendapatkan() { return `${nama ini} ${nama belakang ini}`; }, set(nilai) { [nama.ini, nama belakang ini] = nilai.split(" "); } }); alert(pengguna.Nama Lengkap); // John Smith for(biarkan memasukkan pengguna) alert(key); // nama, nama keluarga
Harap dicatat bahwa properti dapat berupa pengakses (memiliki metode get/set
) atau properti data (memiliki value
), tidak keduanya.
Jika kami mencoba menyediakan get
dan value
dalam deskriptor yang sama, akan terjadi kesalahan:
// Kesalahan: Deskriptor properti tidak valid. Objek.defineProperty({}, 'prop', { mendapatkan() { kembali 1 }, nilai: 2 });
Getter/setter dapat digunakan sebagai pembungkus nilai properti “nyata” untuk mendapatkan kontrol lebih besar atas operasi dengan properti tersebut.
Misalnya, jika kita ingin melarang nama yang terlalu pendek untuk user
, kita dapat memiliki name
penyetel dan menyimpan nilainya di properti terpisah _name
:
biarkan pengguna = { dapatkan nama() { kembalikan ini._nama; }, tetapkan nama(nilai) { if (nilai.panjang < 4) { alert("Nama terlalu pendek, minimal memerlukan 4 karakter"); kembali; } this._name = nilai; } }; pengguna.nama = "Pete"; alert(nama pengguna); // Pete nama pengguna = ""; // Nama terlalu pendek...
Jadi, nama disimpan di properti _name
, dan akses dilakukan melalui pengambil dan penyetel.
Secara teknis, kode eksternal dapat mengakses nama secara langsung dengan menggunakan user._name
. Namun terdapat konvensi umum bahwa properti yang dimulai dengan garis bawah "_"
bersifat internal dan tidak boleh disentuh dari luar objek.
Salah satu kegunaan hebat dari pengakses adalah mereka memungkinkan untuk mengambil kendali atas properti data "biasa" kapan saja dengan menggantinya dengan pengambil dan penyetel dan mengubah perilakunya.
Bayangkan kita mulai mengimplementasikan objek pengguna menggunakan properti data name
dan age
:
fungsi Pengguna(nama, umur) { ini.nama = nama; this.usia = usia; } biarkan john = Pengguna baru("John", 25); waspada( john.usia ); // 25
…Tetapi cepat atau lambat, segalanya mungkin berubah. Daripada age
kami mungkin memutuskan untuk menyimpan birthday
, karena lebih tepat dan nyaman:
fungsi Pengguna(nama, tanggal lahir) { ini.nama = nama; this.birthday = ulang tahun; } biarkan john = Pengguna baru("John", Tanggal baru(1992, 6, 1));
Sekarang apa hubungannya dengan kode lama yang masih menggunakan properti age
?
Kita dapat mencoba menemukan semua tempat tersebut dan memperbaikinya, namun hal tersebut memerlukan waktu dan sulit dilakukan jika kode tersebut digunakan oleh banyak orang. Selain itu, age
adalah hal yang menyenangkan untuk dimiliki user
, bukan?
Mari kita simpan.
Menambahkan pengambil age
memecahkan masalah:
fungsi Pengguna(nama, tanggal lahir) { ini.nama = nama; this.birthday = ulang tahun; // usia dihitung dari tanggal sekarang dan ulang tahun Objek.defineProperty(ini, "usia", { mendapatkan() { biarkan hari iniTahun = Tanggal baru().getFullYear(); kembali hari iniTahun - ini.ulang tahun.getFullYear(); } }); } biarkan john = Pengguna baru("John", Tanggal baru(1992, 6, 1)); peringatan( john.ulang tahun ); // ulang tahun tersedia waspada( john.usia ); // ...serta usianya
Sekarang kode lama juga berfungsi dan kita punya properti tambahan yang bagus.