Tutki edistyneitä JavaScript Proxy -kuvioita objektien sieppausta, validointia ja dynaamista toimintaa varten. Opi parantamaan koodin laatua, turvallisuutta ja ylläpidettävyyttä käytännön esimerkeillä.
JavaScript Proxy-kuviot: Edistynyt objektien sieppaus ja validointi
JavaScript Proxy -objekti on tehokas ominaisuus, jonka avulla voit siepata ja mukauttaa objektin perustoimintoja. Se mahdollistaa edistyneitä metaprogrammointitekniikoita, tarjoten paremman hallinnan objektin käyttäytymiseen ja avaa mahdollisuuksia monimutkaisille suunnittelukuvioille. Tämä artikkeli tutkii erilaisia Proxy-kuvioita, esitellen niiden käyttötapauksia validoinnissa, sieppauksessa ja dynaamisen toiminnan muokkaamisessa. Sukellamme käytännön esimerkkeihin osoittaaksemme, kuinka Proxyt voivat parantaa koodin laatua, turvallisuutta ja ylläpidettävyyttä JavaScript-projekteissasi.
JavaScript Proxyn ymmärtäminen
Pohjimmiltaan Proxy-objekti kapseloi toisen objektin (kohteen) ja sieppaa operaatiot, jotka suoritetaan kyseiselle kohteelle. Nämä sieppaukset hoidetaan loukuilla, jotka ovat metodeja, jotka määrittelevät mukautetun toiminnan tietyille operaatioille, kuten ominaisuuden hakeminen, ominaisuuden asettaminen tai funktion kutsuminen. Proxy API tarjoaa joustavan ja laajennettavan mekanismin objektien oletuskäyttäytymisen muokkaamiseen.
Avainkäsitteet
- Kohde: Alkuperäinen objekti, jonka Proxy kapseloi.
- Käsittelijä: Objekti, joka sisältää loukkumetodit. Jokainen loukku vastaa tiettyä operaatiota.
- Loukut: Käsittelijän metodit, jotka sieppaavat ja mukauttavat objektin operaatioita. Yleisiä loukkuja ovat
get,set,applyjaconstruct.
Proxyn luominen
Luodaksesi Proxyn, käytät Proxy-konstruktoria, joka välittää kohdeobjektin ja käsittelijäobjektin argumentteina:
const target = {};
const handler = {
get: function(target, property, receiver) {
console.log(`Ominaisuuden hakeminen: ${property}`);
return Reflect.get(target, property, receiver);
}
};
const proxy = new Proxy(target, handler);
proxy.name = "John"; // Lokit: Ominaisuuden hakeminen: name
console.log(proxy.name); // Lokit: Ominaisuuden hakeminen: name, sitten John
Yleiset Proxy-loukut
Proxyt tarjoavat valikoiman loukkuja sieppaamaan erilaisia operaatioita. Tässä on joitain yleisimmin käytettyjä loukkuja:
get(target, property, receiver): Sieppaa ominaisuuden pääsyn.set(target, property, value, receiver): Sieppaa ominaisuuden määrityksen.has(target, property): Sieppaain-operaattorin.deleteProperty(target, property): Sieppaadelete-operaattorin.apply(target, thisArg, argumentsList): Sieppaa funktiokutsut.construct(target, argumentsList, newTarget): Sieppaanew-operaattorin.getPrototypeOf(target): SieppaaObject.getPrototypeOf()-metodin.setPrototypeOf(target, prototype): SieppaaObject.setPrototypeOf()-metodin.isExtensible(target): SieppaaObject.isExtensible()-metodin.preventExtensions(target): SieppaaObject.preventExtensions()-metodin.getOwnPropertyDescriptor(target, property): SieppaaObject.getOwnPropertyDescriptor()-metodin.defineProperty(target, property, descriptor): SieppaaObject.defineProperty()-metodin.ownKeys(target): SieppaaObject.getOwnPropertyNames()jaObject.getOwnPropertySymbols()-metodit.
Proxy-kuviot
Tutustutaan nyt joihinkin käytännön Proxy-kuvioihin ja niiden sovelluksiin:
1. Validointi-Proxy
Validointi-Proxy pakottaa rajoituksia ominaisuuksien määrittämiseen. Se sieppaa set-loukun validoidakseen uuden arvon ennen kuin sallii määrityksen jatkamisen.
Esimerkki: Käyttäjän syötteen validointi lomakkeella.
const user = {};
const validator = {
set: function(target, property, value) {
if (property === 'age') {
if (!Number.isInteger(value) || value < 0 || value > 120) {
throw new Error('Virheellinen ikä. Iän on oltava kokonaisluku välillä 0 ja 120.');
}
}
target[property] = value;
return true; // Osoittaa onnistumisen
}
};
const proxy = new Proxy(user, validator);
proxy.name = 'Alice';
proxy.age = 30;
console.log(user);
try {
proxy.age = 'invalid'; // Heittää virheen
} catch (error) {
console.error(error.message);
}
Tässä esimerkissä set-loukku tarkistaa, onko age-ominaisuus kokonaisluku välillä 0 ja 120. Jos validointi epäonnistuu, heitetään virhe, joka estää virheellisen arvon määrittämisen.
Globaali esimerkki: Tämä validointikuvio on välttämätön varmistamaan tietojen eheys globaaleissa sovelluksissa, joissa käyttäjän syöte voi tulla eri lähteistä ja kulttuureista. Esimerkiksi postinumeroiden validointi voi vaihdella merkittävästi maiden välillä. Validointi-Proxy voidaan mukauttaa tukemaan erilaisia validointisääntöjä käyttäjän sijainnin perusteella.
const address = {};
const addressValidator = {
set: function(target, property, value) {
if (property === 'postalCode') {
// Esimerkki: Olettaen yksinkertainen Yhdysvaltojen postinumeron validointi
if (!/^[0-9]{5}(?:-[0-9]{4})?$/.test(value)) {
throw new Error('Virheellinen Yhdysvaltain postinumero.');
}
}
target[property] = value;
return true;
}
};
const addressProxy = new Proxy(address, addressValidator);
addressProxy.postalCode = "12345-6789"; // Kelvollinen
try {
addressProxy.postalCode = "abcde"; // Virheellinen
} catch(e) {
console.log(e);
}
// Kansainvälisemmässä sovelluksessa käyttäisit kehittyneempää validointikirjastoa
// joka voisi validoida postinumerot käyttäjän maan perusteella.
2. Lokitus-Proxy
Lokitus-Proxy sieppaa ominaisuuden pääsyn ja määrityksen lokitakseen nämä operaatiot. Se on hyödyllinen virheenetsintään ja auditoinnissa.
Esimerkki: Ominaisuuden pääsyn ja muokkauksen lokitus.
const data = {
value: 10
};
const logger = {
get: function(target, property) {
console.log(`Ominaisuuden hakeminen: ${property}`);
return target[property];
},
set: function(target, property, value) {
console.log(`Ominaisuuden asettaminen: ${property} arvoon ${value}`);
target[property] = value;
return true;
}
};
const proxy = new Proxy(data, logger);
console.log(proxy.value); // Lokit: Ominaisuuden hakeminen: value, sitten 10
proxy.value = 20; // Lokit: Ominaisuuden asettaminen: value arvoon 20
get- ja set-loukut lokittavat haettavan tai muokatun ominaisuuden, mikä tarjoaa jäljen objektin interaktioista.
Globaali esimerkki: Monikansallisessa yrityksessä lokitus-Proxyja voidaan käyttää tarkastamaan eri paikoissa toimivien työntekijöiden suorittamaa tietojen pääsyä ja muutoksia. Tämä on olennaista vaatimustenmukaisuuden ja turvallisuuden kannalta. Aikavyöhykkeitä saatetaan joutua harkitsemaan lokitustiedoissa.
const employeeData = {
name: "John Doe",
salary: 50000
};
const auditLogger = {
get: function(target, property) {
const timestamp = new Date().toISOString();
console.log(`${timestamp} - [GET] Pääsy ominaisuuteen: ${property}`);
return target[property];
},
set: function(target, property, value) {
const timestamp = new Date().toISOString();
console.log(`${timestamp} - [SET] Ominaisuuden asettaminen: ${property} arvoon ${value}`);
target[property] = value;
return true;
}
};
const proxiedEmployee = new Proxy(employeeData, auditLogger);
proxiedEmployee.name; // Lokittaa aikaleiman ja pääsyn 'name'-ominaisuuteen
proxiedEmployee.salary = 60000; // Lokittaa aikaleiman ja 'salary'-ominaisuuden muokkauksen
3. Vain luku -Proxy
Vain luku -Proxy estää ominaisuuden määrittämisen. Se sieppaa set-loukun ja heittää virheen, jos yritetään muokata ominaisuutta.
Esimerkki: Objektin muuttumattomaksi tekeminen.
const config = {
apiUrl: 'https://api.example.com'
};
const readOnly = {
set: function(target, property, value) {
throw new Error(`Ei voi asettaa ominaisuutta: ${property}. Objekti on vain luku.`);
}
};
const proxy = new Proxy(config, readOnly);
console.log(proxy.apiUrl);
try {
proxy.apiUrl = 'https://newapi.example.com'; // Heittää virheen
} catch (error) {
console.error(error.message);
}
Mikä tahansa yritys asettaa ominaisuuden Proxyyn johtaa virheeseen, mikä varmistaa, että objekti pysyy muuttumattomana.
Globaali esimerkki: Tämä kuvio on hyödyllinen suojaamaan asetustiedostoja, joita ei pitäisi muokata ajonaikaisesti, erityisesti globaalisti jaetuissa sovelluksissa. Asetusten vahingossa tapahtuva muokkaus yhdessä alueella voi vaikuttaa koko järjestelmään.
const globalSettings = {
defaultLanguage: "en",
currency: "USD",
timeZone: "UTC"
};
const immutableHandler = {
set: function(target, property, value) {
throw new Error(`Ei voi muokata vain luku -ominaisuutta: ${property}`);
}
};
const immutableSettings = new Proxy(globalSettings, immutableHandler);
console.log(immutableSettings.defaultLanguage); // tulostaa 'en'
// Yrittäminen muuttaa arvoa aiheuttaa virheen
// immutableSettings.defaultLanguage = "fr"; // heittää Error: Cannot modify read-only property: defaultLanguage
4. Virtuaalinen Proxy
Virtuaalinen Proxy hallitsee pääsyä resurssiin, jonka luominen tai noutaminen voi olla kallista. Se voi viivästyttää resurssin luomista, kunnes sitä todella tarvitaan.
Esimerkki: Laiska kuva, jonka lataus on aloitettu.
const image = {
display: function() {
console.log('Kuvan näyttäminen');
}
};
const virtualProxy = {
get: function(target, property) {
if (property === 'display') {
console.log('Kuvan luominen...');
const realImage = {
display: function() {
console.log('Todellisen kuvan näyttäminen');
}
};
target.display = realImage.display;
return realImage.display;
}
return target[property];
}
};
const proxy = new Proxy(image, virtualProxy);
// Kuvaa ei luoda ennen kuin display-metodia kutsutaan.
proxy.display(); // Lokit: Kuvan luominen..., sitten Todellisen kuvan näyttäminen
Todellinen kuvat-objekti luodaan vasta, kun display-metodia kutsutaan, välttäen tarpeetonta resurssien kulutusta.
Globaali esimerkki: Harkitse globaalia sähköisen kaupankäynnin verkkosivustoa, joka palvelee tuotteiden kuvia. Virtuaalista Proxyä käyttämällä kuvat voidaan ladata vasta, kun ne ovat käyttäjälle näkyvissä, optimoiden kaistanleveyden käyttöä ja parantaen sivun latausaikoja, erityisesti käyttäjille, joilla on hitaat internet-yhteydet eri alueilla.
const product = {
loadImage: function() {
console.log("Ladataan korkearesoluutiokuvaa...");
// Simuloi suuren kuvan lataamista
setTimeout(() => {
console.log("Kuva ladattu");
this.displayImage();
}, 2000);
},
displayImage: function() {
console.log("Näytetään kuva");
}
};
const lazyLoadProxy = {
get: function(target, property) {
if (property === "displayImage") {
// Sen sijaan, että lataisit heti, viivästytä latausta
console.log("Pyyntö näyttää kuva vastaanotettu. Ladataan...");
target.loadImage();
return function() { /* Tarkoituksella Tyhjä */ }; // Palauta tyhjä funktio estämään välitön suoritus
}
return target[property];
}
};
const proxiedProduct = new Proxy(product, lazyLoadProxy);
// Kutsun displayImage käynnistää laiskan latausprosessin
proxiedProduct.displayImage();
5. Peruttava Proxy
Peruttava Proxy mahdollistaa proxyn peruuttamisen milloin tahansa, jolloin se tulee käyttökelvottomaksi. Tämä on hyödyllistä turvallisuusherkissä tilanteissa, joissa sinun on hallittava pääsyä objektiin.
Esimerkki: Väliaikaisen pääsyn myöntäminen resurssiin.
const target = {
secret: 'Tämä on salaisuus'
};
const handler = {
get: function(target, property) {
console.log('Pääsy salaisuuteen');
return target[property];
}
};
const { proxy, revoke } = Proxy.revocable(target, handler);
console.log(proxy.secret); // Lokit: Pääsy salaisuuteen, sitten Tämä on salaisuus
revoke();
try {
console.log(proxy.secret); // Heittää TypeErrorin
} catch (error) {
console.error(error.message); // Lokit: Ei voi suorittaa 'get' Proxyllä, joka on peruttu
}
Proxy.revocable()-metodi luo peruttavan proxyn. revoke()-funktion kutsuminen tekee proxysta käyttökelvottoman, estäen edelleen pääsyn kohdeobjektiin.
Globaali esimerkki: Globaalisti jakautuneessa järjestelmässä voit käyttää peruttavaa Proxyä myöntääksesi väliaikaisen pääsyn arkaluonteisiin tietoihin palvelulle, joka toimii tietyllä alueella. Tietyn ajan kuluttua Proxy voidaan peruuttaa luvattoman pääsyn estämiseksi.
const sensitiveData = {
apiKey: "SUPER_SECRET_KEY"
};
const handler = {
get: function(target, property) {
console.log("Pääsy arkaluontoisiin tietoihin");
return target[property];
}
};
const { proxy: dataProxy, revoke: revokeAccess } = Proxy.revocable(sensitiveData, handler);
// Salli pääsy 5 sekunniksi
setTimeout(() => {
revokeAccess();
console.log("Pääsy peruutettu");
}, 5000);
// Yritä päästä tietoihin
console.log(dataProxy.apiKey); // Lokittaa API-avaimen
// 5 sekunnin kuluttua tämä heittää virheen
setTimeout(() => {
try {
console.log(dataProxy.apiKey); // Heittää: TypeError: Ei voi suorittaa 'get' Proxyllä, joka on peruttu
} catch (error) {
console.error(error);
}
}, 6000);
6. Tyyppimuunnos-Proxy
Tyyppimuunnos-Proxy sieppaa ominaisuuden pääsyn muuntaakseen palautetun arvon automaattisesti tiettyyn tyyppiin. Tämä voi olla hyödyllistä työskenneltäessä eri lähteistä peräisin olevien tietojen kanssa, joilla voi olla epäyhtenäisiä tyyppejä.
Esimerkki: Merkkijonoarvojen muuntaminen numeroiksi.
const data = {
price: '10.99',
quantity: '5'
};
const typeConverter = {
get: function(target, property) {
const value = target[property];
if (typeof value === 'string' && !isNaN(Number(value))) {
return Number(value);
}
return value;
}
};
const proxy = new Proxy(data, typeConverter);
console.log(proxy.price + 1); // Lokit: 11.99 (numero)
console.log(proxy.quantity * 2); // Lokit: 10 (numero)
get-loukku tarkistaa, onko ominaisuuden arvo merkkijono, joka voidaan muuntaa numeroksi. Jos on, se muuntaa arvon numeroksi ennen kuin palauttaa sen.
Globaali esimerkki: Käsitellessäsi tietoja, jotka tulevat eri muotoilukonventioita käyttävistä API:ista (esim. eri päivämäärämuotoja tai valuuttasymboleja), Tyyppimuunnos-Proxy voi varmistaa tietojen yhdenmukaisuuden sovelluksessasi riippumatta lähteestä. Esimerkiksi eri päivämäärämuotojen käsitteleminen ja niiden muuntaminen ISO 8601 -muotoon.
const apiData = {
dateUS: "12/31/2023",
dateEU: "31/12/2023"
};
const dateFormatConverter = {
get: function(target, property) {
let value = target[property];
if (property.startsWith("date")) {
// Yritä muuntaa sekä Yhdysvaltojen että EU:n päivämäärämuodot ISO 8601 -muotoon
if (property === "dateUS") {
const [month, day, year] = value.split("/");
value = `${year}-${month}-${day}`;
} else if (property === "dateEU") {
const [day, month, year] = value.split("/");
value = `${year}-${month}-${day}`;
}
return value;
}
return value;
}
};
const proxiedApiData = new Proxy(apiData, dateFormatConverter);
console.log(proxiedApiData.dateUS); // Tulostaa: 2023-12-31
console.log(proxiedApiData.dateEU); // Tulostaa: 2023-12-31
Parhaat käytännöt Proxyjen käytössä
- Käytä Proxyjä harkiten: Proxyt voivat lisätä monimutkaisuutta koodiisi. Käytä niitä vain, kun ne tarjoavat merkittäviä etuja, kuten parannetun validointi, lokituksen tai objektin käyttäytymisen hallinnan.
- Harkitse suorituskykyä: Proxy-loukut voivat aiheuttaa ylimääräistä kuormitusta. Profiloi koodisi varmistaaksesi, että Proxyt eivät vaikuta negatiivisesti suorituskykyyn, erityisesti suorituskykykriittisissä osissa.
- Käsittele virheet sujuvasti: Varmista, että loukkumetodisi käsittelevät virheet asianmukaisesti ja antavat informatiivisia virheilmoituksia tarvittaessa.
- Käytä Reflect API:ta:
ReflectAPI tarjoaa metodeja, jotka peilaavat objektitoimintojen oletuskäyttäytymistä. KäytäReflect-metodeja loukkumetodeissasi delegoidaksesi alkuperäiseen toimintaan tarvittaessa. Tämä varmistaa, että loukkusi eivät riko olemassa olevia toimintoja. - Dokumentoi Proxysi: Dokumentoi selkeästi Proxyjesi tarkoitus ja käyttäytyminen, mukaan lukien käytetyt loukut ja pakotteet. Tämä auttaa muita kehittäjiä ymmärtämään ja ylläpitämään koodiasi.
Johtopäätös
JavaScript Proxyt ovat tehokas työkalu edistyneeseen objektin manipulointiin ja sieppaukseen. Ymmärtämällä ja soveltamalla erilaisia Proxy-kuvioita voit parantaa koodin laatua, turvallisuutta ja ylläpidettävyyttä. Käyttäjän syötteen validointista arkaluonteisten resurssien pääsyn hallintaan, Proxyt tarjoavat joustavan ja laajennettavan mekanismin objektin käyttäytymisen mukauttamiseen. Kun tutkit Proxyjen mahdollisuuksia, muista käyttää niitä harkiten ja dokumentoida koodisi perusteellisesti.
Annetut esimerkit osoittavat, kuinka JavaScript Proxyjä käytetään ratkaisemaan todellisia ongelmia globaalissa kontekstissa. Ymmärtämällä ja soveltamalla näitä kuvioita voit luoda vankempia, turvallisempia ja ylläpidettävämpiä sovelluksia, jotka vastaavat monipuolisen käyttäjäkunnan tarpeita. Muista aina ottaa huomioon koodisi globaalit vaikutukset ja mukauttaa ratkaisusi eri alueiden ja kulttuurien erityisvaatimuksiin.