Istražite JavaScript Proxy obrasce za modifikaciju ponašanja objekata. Naučite o validaciji, virtualizaciji, praćenju i drugim naprednim tehnikama s primjerima koda.
JavaScript Proxy Obrasci: Ovladavanje Modifikacijom Ponašanja Objekata
JavaScript Proxy objekt pruža snažan mehanizam za presretanje i prilagodbu temeljnih operacija na objektima. Ova sposobnost otvara vrata širokom rasponu dizajnerskih obrazaca i naprednih tehnika za kontrolu ponašanja objekata. Ovaj sveobuhvatni vodič istražuje različite Proxy obrasce, ilustrirajući njihovu upotrebu praktičnim primjerima koda.
Što je JavaScript Proxy?
Proxy objekt omotava drugi objekt (cilj) i presreće njegove operacije. Te operacije, poznate kao zamke (traps), uključuju dohvaćanje svojstava, dodjelu, nabrajanje i pozivanje funkcija. Proxy vam omogućuje da definirate prilagođenu logiku koja će se izvršiti prije, poslije ili umjesto tih operacija. Temeljni koncept Proxyja uključuje "metaprogramiranje" koje vam omogućuje manipuliranje ponašanjem samog JavaScript jezika.
Osnovna sintaksa za stvaranje Proxyja je:
const proxy = new Proxy(target, handler);
- target: Originalni objekt koji želite proksirati.
- handler: Objekt koji sadrži metode (zamke) koje definiraju kako Proxy presreće operacije na cilju.
Uobičajene Proxy Zamke
Handler objekt može definirati nekoliko zamki. Ovdje su neke od najčešće korištenih:
- get(target, property, receiver): Presreće pristup svojstvu (npr.,
obj.property
). - set(target, property, value, receiver): Presreće dodjelu svojstva (npr.,
obj.property = value
). - has(target, property): Presreće
in
operator (npr.,'property' in obj
). - deleteProperty(target, property): Presreće
delete
operator (npr.,delete obj.property
). - apply(target, thisArg, argumentsList): Presreće pozive funkcija (kada je cilj funkcija).
- construct(target, argumentsList, newTarget): Presreće
new
operator (kada je cilj konstruktorska funkcija). - getPrototypeOf(target): Presreće pozive
Object.getPrototypeOf()
. - setPrototypeOf(target, prototype): Presreće pozive
Object.setPrototypeOf()
. - isExtensible(target): Presreće pozive
Object.isExtensible()
. - preventExtensions(target): Presreće pozive
Object.preventExtensions()
. - getOwnPropertyDescriptor(target, property): Presreće pozive
Object.getOwnPropertyDescriptor()
. - defineProperty(target, property, descriptor): Presreće pozive
Object.defineProperty()
. - ownKeys(target): Presreće pozive
Object.getOwnPropertyNames()
iObject.getOwnPropertySymbols()
.
Proxy Obrasci i Slučajevi Upotrebe
Istražimo neke uobičajene Proxy obrasce i kako se mogu primijeniti u stvarnim scenarijima:
1. Validacija
Obrazac validacije koristi Proxy za nametanje ograničenja pri dodjeli svojstava. Ovo je korisno za osiguravanje integriteta podataka.
const validator = {
set: function(obj, prop, value) {
if (prop === 'age') {
if (!Number.isInteger(value)) {
throw new TypeError('Dob mora biti cijeli broj');
}
if (value < 0) {
throw new RangeError('Dob mora biti nenegativan cijeli broj');
}
}
// Zadano ponašanje za spremanje vrijednosti
obj[prop] = value;
// Označava uspjeh
return true;
}
};
let person = {};
let proxy = new Proxy(person, validator);
proxy.age = 25; // Valjano
console.log(proxy.age); // Ispis: 25
try {
proxy.age = 'young'; // Baca TypeError
} catch (e) {
console.log(e); // Ispis: TypeError: Dob mora biti cijeli broj
}
try {
proxy.age = -10; // Baca RangeError
} catch (e) {
console.log(e); // Ispis: RangeError: Dob mora biti nenegativan cijeli broj
}
Primjer: Zamislite platformu za e-trgovinu gdje korisnički podaci zahtijevaju validaciju. Proxy može nametnuti pravila za dob, format e-pošte, jačinu lozinke i druga polja, sprječavajući pohranu nevažećih podataka.
2. Virtualizacija (Lijeno Učitavanje)
Virtualizacija, poznata i kao lijeno učitavanje, odgađa učitavanje skupih resursa dok nisu stvarno potrebni. Proxy može djelovati kao zamjenski objekt za stvarni objekt, učitavajući ga tek kada se pristupi nekom svojstvu.
const expensiveData = {
load: function() {
console.log('Učitavanje skupih podataka...');
// Simulacija vremenski zahtjevne operacije (npr. dohvaćanje iz baze podataka)
return new Promise(resolve => {
setTimeout(() => {
resolve({
data: 'Ovo su skupi podaci'
});
}, 2000);
});
}
};
const lazyLoadHandler = {
get: function(target, prop) {
if (prop === 'data') {
console.log('Pristupanje podacima, učitavanje ako je potrebno...');
return target.load().then(result => {
target.data = result.data; // Spremanje učitanih podataka
return result.data;
});
} else {
return target[prop];
}
}
};
const lazyData = new Proxy(expensiveData, lazyLoadHandler);
console.log('Početni pristup...');
lazyData.data.then(data => {
console.log('Podaci:', data); // Ispis: Podaci: Ovo su skupi podaci
});
console.log('Naknadni pristup...');
lazyData.data.then(data => {
console.log('Podaci:', data); // Ispis: Podaci: Ovo su skupi podaci (učitano iz predmemorije)
});
Primjer: Zamislite veliku društvenu mrežu s korisničkim profilima koji sadrže brojne detalje i povezane medije. Trenutno učitavanje svih podataka profila može biti neučinkovito. Virtualizacija s Proxyjem omogućuje prvo učitavanje osnovnih informacija o profilu, a zatim učitavanje dodatnih detalja ili medijskih sadržaja tek kada korisnik prijeđe na te odjeljke.
3. Zapisivanje i Praćenje
Proxyji se mogu koristiti za praćenje pristupa svojstvima i njihovih izmjena. To je vrijedno za otklanjanje grešaka, reviziju i praćenje performansi.
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} na ${value}`);
target[prop] = value;
return true;
}
};
let obj = { name: 'Alice' };
let proxy = new Proxy(obj, logHandler);
console.log(proxy.name); // Ispis: GET name, Alice
proxy.age = 30; // Ispis: SET age na 30
Primjer: U aplikaciji za kolaborativno uređivanje dokumenata, Proxy može pratiti svaku promjenu napravljenu na sadržaju dokumenta. To omogućuje stvaranje revizijskog traga, omogućavanje funkcionalnosti poništavanja/ponavljanja (undo/redo) i pružanje uvida u doprinose korisnika.
4. Prikazi Samo za Čitanje
Proxyji mogu stvoriti prikaze objekata samo za čitanje, sprječavajući slučajne izmjene. Ovo je korisno za zaštitu osjetljivih podataka.
const readOnlyHandler = {
set: function(target, prop, value) {
console.error(`Ne mogu postaviti svojstvo ${prop}: objekt je samo za čitanje`);
return false; // Označava da operacija postavljanja nije uspjela
},
deleteProperty: function(target, prop) {
console.error(`Ne mogu izbrisati svojstvo ${prop}: objekt je samo za čitanje`);
return false; // Označava da operacija brisanja nije uspjela
}
};
let data = { name: 'Bob', age: 40 };
let readOnlyData = new Proxy(data, readOnlyHandler);
try {
readOnlyData.age = 41; // Baca grešku
} catch (e) {
console.log(e); // Nema bačene greške jer 'set' zamka vraća false.
}
try {
delete readOnlyData.name; // Baca grešku
} catch (e) {
console.log(e); // Nema bačene greške jer 'deleteProperty' zamka vraća false.
}
console.log(data.age); // Ispis: 40 (nepromijenjeno)
Primjer: Razmotrite financijski sustav gdje neki korisnici imaju pristup informacijama o računu samo za čitanje. Proxy se može koristiti za sprječavanje tih korisnika da mijenjaju stanja računa ili druge kritične podatke.
5. Zadane Vrijednosti
Proxy može pružiti zadane vrijednosti za nedostajuća svojstva. To pojednostavljuje kod i izbjegava provjere za null/undefined.
const defaultValuesHandler = {
get: function(target, prop, receiver) {
if (!(prop in target)) {
console.log(`Svojstvo ${prop} nije pronađeno, vraćam zadanu vrijednost.`);
return 'Zadana Vrijednost'; // Ili bilo koja druga prikladna zadana vrijednost
}
return Reflect.get(target, prop, receiver);
}
};
let config = { apiUrl: 'https://api.example.com' };
let configWithDefaults = new Proxy(config, defaultValuesHandler);
console.log(configWithDefaults.apiUrl); // Ispis: https://api.example.com
console.log(configWithDefaults.timeout); // Ispis: Svojstvo timeout nije pronađeno, vraćam zadanu vrijednost. Zadana Vrijednost
Primjer: U sustavu za upravljanje konfiguracijom, Proxy može pružiti zadane vrijednosti za nedostajuće postavke. Na primjer, ako konfiguracijska datoteka ne specificira vremensko ograničenje za povezivanje s bazom podataka, Proxy može vratiti unaprijed definiranu zadanu vrijednost.
6. Metapodaci i Anotacije
Proxyji mogu priložiti metapodatke ili anotacije objektima, pružajući dodatne informacije bez mijenjanja originalnog objekta.
const metadataHandler = {
get: function(target, prop, receiver) {
if (prop === '__metadata__') {
return { description: 'Ovo su metapodaci za objekt' };
}
return Reflect.get(target, prop, receiver);
}
};
let article = { title: 'Uvod u Proxyje', content: '...' };
let articleWithMetadata = new Proxy(article, metadataHandler);
console.log(articleWithMetadata.title); // Ispis: Uvod u Proxyje
console.log(articleWithMetadata.__metadata__.description); // Ispis: Ovo su metapodaci za objekt
Primjer: U sustavu za upravljanje sadržajem, Proxy može priložiti metapodatke člancima, poput informacija o autoru, datumu objave i ključnim riječima. Ti se metapodaci mogu koristiti za pretraživanje, filtriranje i kategoriziranje sadržaja.
7. Presretanje Funkcija
Proxyji mogu presretati pozive funkcija, omogućujući vam dodavanje zapisivanja, validacije ili druge logike prije ili poslije obrade.
const functionInterceptor = {
apply: function(target, thisArg, argumentsList) {
console.log('Pozivanje funkcije s argumentima:', argumentsList);
const result = target.apply(thisArg, argumentsList);
console.log('Funkcija je vratila:', result);
return result;
}
};
function add(a, b) {
return a + b;
}
let proxiedAdd = new Proxy(add, functionInterceptor);
let sum = proxiedAdd(5, 3); // Ispis: Pozivanje funkcije s argumentima: [5, 3], Funkcija je vratila: 8
console.log(sum); // Ispis: 8
Primjer: U bankarskoj aplikaciji, Proxy može presretati pozive transakcijskim funkcijama, zapisujući svaku transakciju i provodeći provjere za otkrivanje prijevara prije izvršenja transakcije.
8. Presretanje Konstruktora
Proxyji mogu presretati pozive konstruktora, omogućujući vam prilagodbu stvaranja objekata.
const constructorInterceptor = {
construct: function(target, argumentsList, newTarget) {
console.log('Stvaranje nove instance klase', target.name, 's argumentima:', argumentsList);
const obj = new target(...argumentsList);
console.log('Nova instanca stvorena:', 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); // Ispis: Stvaranje nove instance klase Person s argumentima: ['Alice', 28], Nova instanca stvorena: Person { name: 'Alice', age: 28 }
console.log(person);
Primjer: U okviru za razvoj igara, Proxy može presretati stvaranje objekata u igri, automatski dodjeljujući jedinstvene ID-ove, dodajući zadane komponente i registrirajući ih s pogonom igre.
Napredna Razmatranja
- Performanse: Iako Proxyji nude fleksibilnost, mogu uvesti pad performansi. Važno je testirati i profilirati vaš kod kako biste osigurali da prednosti korištenja Proxyja nadmašuju troškove performansi, posebno u aplikacijama kritičnim za performanse.
- Kompatibilnost: Proxyji su relativno nov dodatak JavaScriptu, stoga ih stariji preglednici možda ne podržavaju. Koristite detekciju značajki ili polyfillove kako biste osigurali kompatibilnost sa starijim okruženjima.
- Opozivi Proxyji: Metoda
Proxy.revocable()
stvara Proxy koji se može opozvati. Opozivanjem Proxyja sprječava se daljnje presretanje operacija. To može biti korisno u svrhe sigurnosti ili upravljanja resursima. - Reflect API: Reflect API pruža metode za izvršavanje zadanog ponašanja Proxy zamki. Korištenje
Reflect
osigurava da se vaš Proxy kod ponaša u skladu sa specifikacijom jezika.
Zaključak
JavaScript Proxyji pružaju snažan i svestran mehanizam za prilagodbu ponašanja objekata. Ovladavanjem različitim Proxy obrascima, možete pisati robusniji, održiviji i učinkovitiji kod. Bilo da implementirate validaciju, virtualizaciju, praćenje ili druge napredne tehnike, Proxyji nude fleksibilno rješenje za kontrolu pristupa i manipulacije objektima. Uvijek uzmite u obzir implikacije na performanse i osigurajte kompatibilnost s vašim ciljnim okruženjima. Proxyji su ključan alat u arsenalu modernog JavaScript programera, omogućujući moćne tehnike metaprogramiranja.
Daljnje Istraživanje
- Mozilla Developer Network (MDN): JavaScript Proxy
- Istraživanje JavaScript Proxyja: Članak na Smashing Magazineu