Tutustu JavaScript Proxy -malleihin olioiden käyttäytymisen muokkaamisessa. Opi validointi, virtualisointi, seuranta ja muut edistyneet tekniikat koodiesimerkkien avulla.
JavaScript Proxy-mallit: Olion käyttäytymisen muokkaamisen hallinta
JavaScriptin Proxy-olio tarjoaa tehokkaan mekanismin olioiden perustoimintojen sieppaamiseen ja mukauttamiseen. Tämä ominaisuus avaa ovet monenlaisille suunnittelumalleille ja edistyneille tekniikoille olioiden käyttäytymisen hallintaan. Tämä kattava opas tutkii erilaisia Proxy-malleja ja havainnollistaa niiden käyttöä käytännön koodiesimerkeillä.
Mikä on JavaScript Proxy?
Proxy-olio käärii toisen olion (kohteen) ja sieppaa sen toiminnot. Nämä toiminnot, joita kutsutaan ansoiksi (traps), sisältävät ominaisuuksien haun, sijoituksen, luetteloinnin ja funktion kutsun. Proxy mahdollistaa mukautetun logiikan määrittelyn, joka suoritetaan ennen näitä toimintoja, niiden jälkeen tai niiden sijasta. Proxyn ydinajatus liittyy "metaohjelmointiin", joka mahdollistaa itse JavaScript-kielen käyttäytymisen manipuloinnin.
Proxyn luomisen perussyntaksi on:
const proxy = new Proxy(target, handler);
- target: Alkuperäinen olio, jonka haluat välittää proxyn kautta.
- handler: Olio, joka sisältää metodeja (ansoja), jotka määrittelevät, kuinka Proxy sieppaa kohteeseen kohdistuvia toimintoja.
Yleiset Proxy-ansat
Käsittelijäolio (handler) voi määritellä useita ansoja. Tässä on joitakin yleisimmin käytettyjä:
- get(target, property, receiver): Sieppaa ominaisuuden käytön (esim.
obj.property
). - set(target, property, value, receiver): Sieppaa ominaisuuden sijoituksen (esim.
obj.property = value
). - has(target, property): Sieppaa
in
-operaattorin (esim.'property' in obj
). - deleteProperty(target, property): Sieppaa
delete
-operaattorin (esim.delete obj.property
). - apply(target, thisArg, argumentsList): Sieppaa funktiokutsut (kun kohde on funktio).
- construct(target, argumentsList, newTarget): Sieppaa
new
-operaattorin (kun kohde on konstruktorifunktio). - getPrototypeOf(target): Sieppaa kutsut
Object.getPrototypeOf()
-metodiin. - setPrototypeOf(target, prototype): Sieppaa kutsut
Object.setPrototypeOf()
-metodiin. - isExtensible(target): Sieppaa kutsut
Object.isExtensible()
-metodiin. - preventExtensions(target): Sieppaa kutsut
Object.preventExtensions()
-metodiin. - getOwnPropertyDescriptor(target, property): Sieppaa kutsut
Object.getOwnPropertyDescriptor()
-metodiin. - defineProperty(target, property, descriptor): Sieppaa kutsut
Object.defineProperty()
-metodiin. - ownKeys(target): Sieppaa kutsut
Object.getOwnPropertyNames()
- jaObject.getOwnPropertySymbols()
-metodeihin.
Proxy-mallit ja käyttötapaukset
Tutustutaanpa joihinkin yleisiin Proxy-malleihin ja siihen, miten niitä voidaan soveltaa todellisissa tilanteissa:
1. Validointi
Validointimalli käyttää Proxya pakottamaan rajoituksia ominaisuuksien sijoituksille. Tämä on hyödyllistä datan eheyden varmistamisessa.
const validator = {
set: function(obj, prop, value) {
if (prop === 'age') {
if (!Number.isInteger(value)) {
throw new TypeError('Ikä ei ole kokonaisluku');
}
if (value < 0) {
throw new RangeError('Iän on oltava ei-negatiivinen kokonaisluku');
}
}
// Oletustoiminto arvon tallentamiseksi
obj[prop] = value;
// Ilmaisee onnistumisen
return true;
}
};
let person = {};
let proxy = new Proxy(person, validator);
proxy.age = 25; // Kelvollinen
console.log(proxy.age); // Tuloste: 25
try {
proxy.age = 'young'; // Heittää TypeError-virheen
} catch (e) {
console.log(e); // Tuloste: TypeError: Ikä ei ole kokonaisluku
}
try {
proxy.age = -10; // Heittää RangeError-virheen
} catch (e) {
console.log(e); // Tuloste: RangeError: Iän on oltava ei-negatiivinen kokonaisluku
}
Esimerkki: Kuvitellaan verkkokauppa-alusta, jossa käyttäjätiedot vaativat validointia. Proxy voi pakottaa sääntöjä iälle, sähköpostimuodolle, salasanan vahvuudelle ja muille kentille, estäen virheellisen datan tallentamisen.
2. Virtualisointi (Viivästetty lataus)
Virtualisointi, joka tunnetaan myös nimellä viivästetty lataus (lazy loading), viivästyttää kalliiden resurssien lataamista, kunnes niitä todella tarvitaan. Proxy voi toimia paikkamerkkinä todelliselle oliolle, ladaten sen vasta, kun ominaisuutta käytetään.
const expensiveData = {
load: function() {
console.log('Ladataan kallista dataa...');
// Simuloi aikaa vievää operaatiota (esim. nouto tietokannasta)
return new Promise(resolve => {
setTimeout(() => {
resolve({
data: 'Tämä on kallis data'
});
}, 2000);
});
}
};
const lazyLoadHandler = {
get: function(target, prop) {
if (prop === 'data') {
console.log('Käytetään dataa, ladataan tarvittaessa...');
return target.load().then(result => {
target.data = result.data; // Tallenna ladattu data
return result.data;
});
} else {
return target[prop];
}
}
};
const lazyData = new Proxy(expensiveData, lazyLoadHandler);
console.log('Ensimmäinen käyttökerta...');
lazyData.data.then(data => {
console.log('Data:', data); // Tuloste: Data: Tämä on kallis data
});
console.log('Seuraava käyttökerta...');
lazyData.data.then(data => {
console.log('Data:', data); // Tuloste: Data: Tämä on kallis data (ladattu välimuistista)
});
Esimerkki: Kuvittele suuri sosiaalisen median alusta, jossa käyttäjäprofiilit sisältävät lukuisia tietoja ja niihin liittyvää mediaa. Kaikkien profiilitietojen lataaminen välittömästi voi olla tehotonta. Virtualisointi Proxyn avulla mahdollistaa ensin perustietojen lataamisen ja sitten lisätietojen tai mediasisällön lataamisen vasta, kun käyttäjä siirtyy kyseisiin osioihin.
3. Lokitus ja seuranta
Proxyja voidaan käyttää ominaisuuksien käytön ja muokkausten seurantaan. Tämä on arvokasta virheenkorjauksessa, auditoinnissa ja suorituskyvyn valvonnassa.
const logHandler = {
get: function(target, prop, receiver) {
console.log(`HAKU ${prop}`);
return Reflect.get(target, prop, receiver);
},
set: function(target, prop, value) {
console.log(`ASETUS ${prop} arvoksi ${value}`);
target[prop] = value;
return true;
}
};
let obj = { name: 'Alice' };
let proxy = new Proxy(obj, logHandler);
console.log(proxy.name); // Tuloste: HAKU name, Alice
proxy.age = 30; // Tuloste: ASETUS age arvoksi 30
Esimerkki: Yhteiskäyttöisessä dokumenttien muokkaussovelluksessa Proxy voi seurata jokaista dokumentin sisältöön tehtyä muutosta. Tämä mahdollistaa auditointijäljen luomisen, kumoa/tee uudelleen -toiminnallisuuden ja antaa tietoa käyttäjien tekemistä muutoksista.
4. Vain luku -näkymät
Proxyilla voidaan luoda olioista vain luku -näkymiä, jotka estävät vahingossa tehtävät muutokset. Tämä on hyödyllistä arkaluontoisten tietojen suojaamisessa.
const readOnlyHandler = {
set: function(target, prop, value) {
console.error(`Ominaisuutta ${prop} ei voi asettaa: olio on vain luku -tilassa`);
return false; // Ilmaisee, että asetusoperaatio epäonnistui
},
deleteProperty: function(target, prop) {
console.error(`Ominaisuutta ${prop} ei voi poistaa: olio on vain luku -tilassa`);
return false; // Ilmaisee, että poisto-operaatio epäonnistui
}
};
let data = { name: 'Bob', age: 40 };
let readOnlyData = new Proxy(data, readOnlyHandler);
try {
readOnlyData.age = 41; // Heittää virheen
} catch (e) {
console.log(e); // Virhettä ei heitetä, koska 'set'-ansa palauttaa false.
}
try {
delete readOnlyData.name; // Heittää virheen
} catch (e) {
console.log(e); // Virhettä ei heitetä, koska 'deleteProperty'-ansa palauttaa false.
}
console.log(data.age); // Tuloste: 40 (muuttumaton)
Esimerkki: Kuvitellaan rahoitusjärjestelmä, jossa joillakin käyttäjillä on vain lukuoikeus tilitietoihin. Proxya voidaan käyttää estämään näitä käyttäjiä muokkaamasta tilisaldoja tai muita kriittisiä tietoja.
5. Oletusarvot
Proxy voi tarjota oletusarvoja puuttuville ominaisuuksille. Tämä yksinkertaistaa koodia ja välttää null/undefined-tarkistuksia.
const defaultValuesHandler = {
get: function(target, prop, receiver) {
if (!(prop in target)) {
console.log(`Ominaisuutta ${prop} ei löytynyt, palautetaan oletusarvo.`);
return 'Oletusarvo'; // Tai jokin muu sopiva oletusarvo
}
return Reflect.get(target, prop, receiver);
}
};
let config = { apiUrl: 'https://api.example.com' };
let configWithDefaults = new Proxy(config, defaultValuesHandler);
console.log(configWithDefaults.apiUrl); // Tuloste: https://api.example.com
console.log(configWithDefaults.timeout); // Tuloste: Ominaisuutta timeout ei löytynyt, palautetaan oletusarvo. Oletusarvo
Esimerkki: Konfiguraationhallintajärjestelmässä Proxy voi tarjota oletusarvoja puuttuville asetuksille. Esimerkiksi, jos konfiguraatiotiedosto ei määritä tietokantayhteyden aikakatkaisua, Proxy voi palauttaa ennalta määritellyn oletusarvon.
6. Metadata ja annotaatiot
Proxyilla voidaan liittää metadataa tai annotaatioita olioihin, tarjoten lisätietoa muuttamatta alkuperäistä oliota.
const metadataHandler = {
get: function(target, prop, receiver) {
if (prop === '__metadata__') {
return { description: 'Tämä on olion metadataa' };
}
return Reflect.get(target, prop, receiver);
}
};
let article = { title: 'Johdanto Proxyihin', content: '...' };
let articleWithMetadata = new Proxy(article, metadataHandler);
console.log(articleWithMetadata.title); // Tuloste: Johdanto Proxyihin
console.log(articleWithMetadata.__metadata__.description); // Tuloste: Tämä on olion metadataa
Esimerkki: Sisällönhallintajärjestelmässä Proxy voi liittää metadataa artikkeleihin, kuten tekijätiedot, julkaisupäivämäärän ja avainsanat. Tätä metadataa voidaan käyttää sisällön etsimiseen, suodattamiseen ja luokitteluun.
7. Funktion sieppaus
Proxyilla voidaan siepata funktiokutsuja, mikä mahdollistaa lokituksen, validoinnin tai muun esi- tai jälkikäsittelylogiikan lisäämisen.
const functionInterceptor = {
apply: function(target, thisArg, argumentsList) {
console.log('Kutsutaan funktiota argumenteilla:', argumentsList);
const result = target.apply(thisArg, argumentsList);
console.log('Funktio palautti:', result);
return result;
}
};
function add(a, b) {
return a + b;
}
let proxiedAdd = new Proxy(add, functionInterceptor);
let sum = proxiedAdd(5, 3); // Tuloste: Kutsutaan funktiota argumenteilla: [5, 3], Funktio palautti: 8
console.log(sum); // Tuloste: 8
Esimerkki: Pankkisovelluksessa Proxy voi siepata kutsut transaktiofunktioihin, lokittaen jokaisen transaktion ja suorittaen petostentorjuntatarkistuksia ennen transaktion suorittamista.
8. Konstruktorin sieppaus
Proxyilla voidaan siepata konstruktorikutsuja, mikä mahdollistaa olioiden luomisen mukauttamisen.
const constructorInterceptor = {
construct: function(target, argumentsList, newTarget) {
console.log('Luodaan uusi ilmentymä luokasta', target.name, 'argumenteilla:', argumentsList);
const obj = new target(...argumentsList);
console.log('Uusi ilmentymä luotu:', 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); // Tuloste: Luodaan uusi ilmentymä luokasta Person argumenteilla: ['Alice', 28], Uusi ilmentymä luotu: Person { name: 'Alice', age: 28 }
console.log(person);
Esimerkki: Pelinkehityskehyksessä Proxy voi siepata peliobjektien luomisen, antaen niille automaattisesti yksilölliset tunnisteet, lisäten oletuskomponentteja ja rekisteröiden ne pelimoottoriin.
Edistyneitä huomioita
- Suorituskyky: Vaikka Proxyt tarjoavat joustavuutta, ne voivat aiheuttaa suorituskykyyn liittyvää ylikuormaa. On tärkeää testata ja profiloida koodia varmistaaksesi, että Proxyjen käytön hyödyt ovat suuremmat kuin suorituskykykustannukset, erityisesti suorituskykykriittisissä sovelluksissa.
- Yhteensopivuus: Proxyt ovat suhteellisen uusi lisäys JavaScriptiin, joten vanhemmat selaimet eivät välttämättä tue niitä. Käytä ominaisuuksien tunnistusta tai polyfillejä varmistaaksesi yhteensopivuuden vanhempien ympäristöjen kanssa.
- Peruutettavat Proxyt:
Proxy.revocable()
-metodi luo Proxyn, joka voidaan peruuttaa. Proxyn peruuttaminen estää kaikkien tulevien toimintojen sieppaamisen. Tämä voi olla hyödyllistä turvallisuus- tai resurssienhallintatarkoituksissa. - Reflect API: Reflect API tarjoaa metodeja Proxy-ansojen oletustoimintojen suorittamiseen.
Reflect
-olion käyttö varmistaa, että Proxy-koodisi toimii yhdenmukaisesti kielimäärityksen kanssa.
Yhteenveto
JavaScript Proxyt tarjoavat tehokkaan ja monipuolisen mekanismin olioiden käyttäytymisen mukauttamiseen. Hallitsemalla erilaisia Proxy-malleja voit kirjoittaa vankempaa, ylläpidettävämpää ja tehokkaampaa koodia. Toteutitpa sitten validointia, virtualisointia, seurantaa tai muita edistyneitä tekniikoita, Proxyt tarjoavat joustavan ratkaisun olioiden käytön ja manipuloinnin hallintaan. Ota aina huomioon suorituskykyvaikutukset ja varmista yhteensopivuus kohdeympäristöjesi kanssa. Proxyt ovat keskeinen työkalu modernin JavaScript-kehittäjän työkalupakissa, mahdollistaen voimakkaat metaohjelmointitekniikat.
Lisätutkimusta
- Mozilla Developer Network (MDN): JavaScript Proxy
- Exploring JavaScript Proxies: Smashing Magazine -artikkeli