Jelajahi pola Proxy JavaScript untuk modifikasi perilaku objek. Pelajari validasi, virtualisasi, pelacakan, dan teknik canggih lainnya dengan contoh kode.
Pola Proxy JavaScript: Menguasai Modifikasi Perilaku Objek
Objek Proxy JavaScript menyediakan mekanisme yang kuat untuk mencegat dan menyesuaikan operasi fundamental pada objek. Kemampuan ini membuka pintu ke berbagai pola desain dan teknik canggih untuk mengendalikan perilaku objek. Panduan komprehensif ini menjelajahi berbagai pola Proxy, mengilustrasikan penggunaannya dengan contoh kode praktis.
Apa itu Proxy JavaScript?
Objek Proxy membungkus objek lain (target) dan mencegat operasinya. Operasi ini, yang dikenal sebagai trap, mencakup pencarian properti, penetapan, enumerasi, dan pemanggilan fungsi. Proxy memungkinkan Anda untuk mendefinisikan logika kustom yang akan dieksekusi sebelum, sesudah, atau sebagai pengganti operasi-operasi ini. Konsep inti dari Proxy melibatkan "metaprogramming" yang memungkinkan Anda untuk memanipulasi perilaku bahasa JavaScript itu sendiri.
Sintaks dasar untuk membuat Proxy adalah:
const proxy = new Proxy(target, handler);
- target: Objek asli yang ingin Anda proksi.
- handler: Objek yang berisi metode (trap) yang mendefinisikan bagaimana Proxy mencegat operasi pada target.
Trap Proxy yang Umum
Objek handler dapat mendefinisikan beberapa trap. Berikut adalah beberapa yang paling umum digunakan:
- get(target, property, receiver): Mencegat akses properti (mis.,
obj.property
). - set(target, property, value, receiver): Mencegat penetapan properti (mis.,
obj.property = value
). - has(target, property): Mencegat operator
in
(mis.,'property' in obj
). - deleteProperty(target, property): Mencegat operator
delete
(mis.,delete obj.property
). - apply(target, thisArg, argumentsList): Mencegat pemanggilan fungsi (ketika target adalah fungsi).
- construct(target, argumentsList, newTarget): Mencegat operator
new
(ketika target adalah fungsi konstruktor). - getPrototypeOf(target): Mencegat pemanggilan ke
Object.getPrototypeOf()
. - setPrototypeOf(target, prototype): Mencegat pemanggilan ke
Object.setPrototypeOf()
. - isExtensible(target): Mencegat pemanggilan ke
Object.isExtensible()
. - preventExtensions(target): Mencegat pemanggilan ke
Object.preventExtensions()
. - getOwnPropertyDescriptor(target, property): Mencegat pemanggilan ke
Object.getOwnPropertyDescriptor()
. - defineProperty(target, property, descriptor): Mencegat pemanggilan ke
Object.defineProperty()
. - ownKeys(target): Mencegat pemanggilan ke
Object.getOwnPropertyNames()
danObject.getOwnPropertySymbols()
.
Pola dan Kasus Penggunaan Proxy
Mari kita jelajahi beberapa pola Proxy yang umum dan bagaimana pola tersebut dapat diterapkan dalam skenario dunia nyata:
1. Validasi
Pola Validasi menggunakan Proxy untuk memberlakukan batasan pada penetapan properti. Ini berguna untuk memastikan integritas data.
const validator = {
set: function(obj, prop, value) {
if (prop === 'age') {
if (!Number.isInteger(value)) {
throw new TypeError('Usia bukan integer');
}
if (value < 0) {
throw new RangeError('Usia harus berupa integer non-negatif');
}
}
// Perilaku default untuk menyimpan nilai
obj[prop] = value;
// Menandakan keberhasilan
return true;
}
};
let person = {};
let proxy = new Proxy(person, validator);
proxy.age = 25; // Valid
console.log(proxy.age); // Output: 25
try {
proxy.age = 'young'; // Melemparkan TypeError
} catch (e) {
console.log(e); // Output: TypeError: Usia bukan integer
}
try {
proxy.age = -10; // Melemparkan RangeError
} catch (e) {
console.log(e); // Output: RangeError: Usia harus berupa integer non-negatif
}
Contoh: Bayangkan sebuah platform e-commerce di mana data pengguna memerlukan validasi. Sebuah proxy dapat memberlakukan aturan pada usia, format email, kekuatan kata sandi, dan bidang lainnya, mencegah data yang tidak valid disimpan.
2. Virtualisasi (Lazy Loading)
Virtualisasi, juga dikenal sebagai lazy loading, menunda pemuatan sumber daya yang mahal hingga benar-benar dibutuhkan. Sebuah Proxy dapat bertindak sebagai placeholder untuk objek nyata, memuatnya hanya ketika sebuah properti diakses.
const expensiveData = {
load: function() {
console.log('Memuat data yang mahal...');
// Mensimulasikan operasi yang memakan waktu (mis., mengambil dari database)
return new Promise(resolve => {
setTimeout(() => {
resolve({
data: 'Ini adalah data yang mahal'
});
}, 2000);
});
}
};
const lazyLoadHandler = {
get: function(target, prop) {
if (prop === 'data') {
console.log('Mengakses data, memuatnya jika perlu...');
return target.load().then(result => {
target.data = result.data; // Simpan data yang telah dimuat
return result.data;
});
} else {
return target[prop];
}
}
};
const lazyData = new Proxy(expensiveData, lazyLoadHandler);
console.log('Akses awal...');
lazyData.data.then(data => {
console.log('Data:', data); // Output: Data: Ini adalah data yang mahal
});
console.log('Akses berikutnya...');
lazyData.data.then(data => {
console.log('Data:', data); // Output: Data: Ini adalah data yang mahal (dimuat dari cache)
});
Contoh: Bayangkan sebuah platform media sosial besar dengan profil pengguna yang berisi banyak detail dan media terkait. Memuat semua data profil secara langsung bisa jadi tidak efisien. Virtualisasi dengan Proxy memungkinkan pemuatan informasi profil dasar terlebih dahulu, dan kemudian memuat detail tambahan atau konten media hanya ketika pengguna menavigasi ke bagian tersebut.
3. Pencatatan dan Pelacakan
Proxy dapat digunakan untuk melacak akses dan modifikasi properti. Ini sangat berharga untuk debugging, audit, dan pemantauan kinerja.
const logHandler = {
get: function(target, prop, receiver) {
console.log(`GET ${prop}`);
return Reflect.get(target, prop, receiver);
},
set: function(target, prop, value) {
console.log(`SET ${prop} ke ${value}`);
target[prop] = value;
return true;
}
};
let obj = { name: 'Alice' };
let proxy = new Proxy(obj, logHandler);
console.log(proxy.name); // Output: GET name, Alice
proxy.age = 30; // Output: SET age ke 30
Contoh: Dalam aplikasi pengeditan dokumen kolaboratif, Proxy dapat melacak setiap perubahan yang dibuat pada konten dokumen. Ini memungkinkan pembuatan jejak audit, mengaktifkan fungsionalitas urungkan/ulangi (undo/redo), dan memberikan wawasan tentang kontribusi pengguna.
4. Tampilan Hanya-Baca (Read-Only Views)
Proxy dapat membuat tampilan objek yang hanya-baca, mencegah modifikasi yang tidak disengaja. Ini berguna untuk melindungi data sensitif.
const readOnlyHandler = {
set: function(target, prop, value) {
console.error(`Tidak dapat mengatur properti ${prop}: objek hanya-baca`);
return false; // Menandakan bahwa operasi set gagal
},
deleteProperty: function(target, prop) {
console.error(`Tidak dapat menghapus properti ${prop}: objek hanya-baca`);
return false; // Menandakan bahwa operasi hapus gagal
}
};
let data = { name: 'Bob', age: 40 };
let readOnlyData = new Proxy(data, readOnlyHandler);
try {
readOnlyData.age = 41; // Melemparkan error
} catch (e) {
console.log(e); // Tidak ada error yang dilemparkan karena trap 'set' mengembalikan false.
}
try {
delete readOnlyData.name; // Melemparkan error
} catch (e) {
console.log(e); // Tidak ada error yang dilemparkan karena trap 'deleteProperty' mengembalikan false.
}
console.log(data.age); // Output: 40 (tidak berubah)
Contoh: Pertimbangkan sistem keuangan di mana beberapa pengguna memiliki akses hanya-baca ke informasi akun. Sebuah Proxy dapat digunakan untuk mencegah pengguna ini memodifikasi saldo akun atau data penting lainnya.
5. Nilai Default
Sebuah Proxy dapat memberikan nilai default untuk properti yang hilang. Ini menyederhanakan kode dan menghindari pemeriksaan null/undefined.
const defaultValuesHandler = {
get: function(target, prop, receiver) {
if (!(prop in target)) {
console.log(`Properti ${prop} tidak ditemukan, mengembalikan nilai default.`);
return 'Nilai Default'; // Atau default lain yang sesuai
}
return Reflect.get(target, prop, receiver);
}
};
let config = { apiUrl: 'https://api.example.com' };
let configWithDefaults = new Proxy(config, defaultValuesHandler);
console.log(configWithDefaults.apiUrl); // Output: https://api.example.com
console.log(configWithDefaults.timeout); // Output: Properti timeout tidak ditemukan, mengembalikan nilai default. Nilai Default
Contoh: Dalam sistem manajemen konfigurasi, sebuah Proxy dapat memberikan nilai default untuk pengaturan yang hilang. Misalnya, jika file konfigurasi tidak menentukan waktu habis koneksi database, Proxy dapat mengembalikan nilai default yang telah ditentukan sebelumnya.
6. Metadata dan Anotasi
Proxy dapat melampirkan metadata atau anotasi ke objek, memberikan informasi tambahan tanpa memodifikasi objek asli.
const metadataHandler = {
get: function(target, prop, receiver) {
if (prop === '__metadata__') {
return { description: 'Ini adalah metadata untuk objek' };
}
return Reflect.get(target, prop, receiver);
}
};
let article = { title: 'Pengantar Proxy', content: '...' };
let articleWithMetadata = new Proxy(article, metadataHandler);
console.log(articleWithMetadata.title); // Output: Pengantar Proxy
console.log(articleWithMetadata.__metadata__.description); // Output: Ini adalah metadata untuk objek
Contoh: Dalam sistem manajemen konten, sebuah Proxy dapat melampirkan metadata ke artikel, seperti informasi penulis, tanggal publikasi, dan kata kunci. Metadata ini dapat digunakan untuk mencari, memfilter, dan mengkategorikan konten.
7. Pencegatan Fungsi
Proxy dapat mencegat pemanggilan fungsi, memungkinkan Anda untuk menambahkan pencatatan, validasi, atau logika pra- atau pasca-pemrosesan lainnya.
const functionInterceptor = {
apply: function(target, thisArg, argumentsList) {
console.log('Memanggil fungsi dengan argumen:', argumentsList);
const result = target.apply(thisArg, argumentsList);
console.log('Fungsi mengembalikan:', result);
return result;
}
};
function add(a, b) {
return a + b;
}
let proxiedAdd = new Proxy(add, functionInterceptor);
let sum = proxiedAdd(5, 3); // Output: Memanggil fungsi dengan argumen: [5, 3], Fungsi mengembalikan: 8
console.log(sum); // Output: 8
Contoh: Dalam aplikasi perbankan, sebuah Proxy dapat mencegat panggilan ke fungsi transaksi, mencatat setiap transaksi dan melakukan pemeriksaan deteksi penipuan sebelum mengeksekusi transaksi.
8. Pencegatan Konstruktor
Proxy dapat mencegat pemanggilan konstruktor, memungkinkan Anda untuk menyesuaikan pembuatan objek.
const constructorInterceptor = {
construct: function(target, argumentsList, newTarget) {
console.log('Membuat instance baru dari', target.name, 'dengan argumen:', argumentsList);
const obj = new target(...argumentsList);
console.log('Instance baru dibuat:', obj);
return obj;
}
};
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
let ProxiedPerson = new Proxy(Person, constructorInterceptor);
let person = new ProxiedPerson('Alice', 28); // Output: Membuat instance baru dari Person dengan argumen: ['Alice', 28], Instance baru dibuat: Person { name: 'Alice', age: 28 }
console.log(person);
Contoh: Dalam kerangka kerja pengembangan game, sebuah Proxy dapat mencegat pembuatan objek game, secara otomatis menetapkan ID unik, menambahkan komponen default, dan mendaftarkannya ke mesin game.
Pertimbangan Lanjutan
- Kinerja: Meskipun Proxy menawarkan fleksibilitas, mereka dapat menimbulkan overhead kinerja. Penting untuk melakukan benchmark dan profil pada kode Anda untuk memastikan bahwa manfaat penggunaan Proxy lebih besar daripada biaya kinerjanya, terutama dalam aplikasi yang kritis terhadap kinerja.
- Kompatibilitas: Proxy adalah tambahan yang relatif baru di JavaScript, jadi browser yang lebih lama mungkin tidak mendukungnya. Gunakan deteksi fitur atau polyfill untuk memastikan kompatibilitas dengan lingkungan yang lebih lama.
- Proxy yang Dapat Dicabut (Revocable Proxies): Metode
Proxy.revocable()
membuat Proxy yang dapat dicabut. Mencabut Proxy mencegah operasi lebih lanjut untuk dicegat. Ini bisa berguna untuk tujuan keamanan atau manajemen sumber daya. - Reflect API: Reflect API menyediakan metode untuk melakukan perilaku default dari trap Proxy. Menggunakan
Reflect
memastikan bahwa kode Proxy Anda berperilaku konsisten dengan spesifikasi bahasa.
Kesimpulan
Proxy JavaScript menyediakan mekanisme yang kuat dan serbaguna untuk menyesuaikan perilaku objek. Dengan menguasai berbagai pola Proxy, Anda dapat menulis kode yang lebih kuat, mudah dipelihara, dan efisien. Baik Anda menerapkan validasi, virtualisasi, pelacakan, atau teknik canggih lainnya, Proxy menawarkan solusi fleksibel untuk mengontrol bagaimana objek diakses dan dimanipulasi. Selalu pertimbangkan implikasi kinerja dan pastikan kompatibilitas dengan lingkungan target Anda. Proxy adalah alat kunci dalam persenjataan pengembang JavaScript modern, yang memungkinkan teknik metaprogramming yang kuat.
Eksplorasi Lebih Lanjut
- Mozilla Developer Network (MDN): JavaScript Proxy
- Menjelajahi Proxy JavaScript: Artikel Smashing Magazine