Naršykite JavaScript Proxy šablonus, skirtus objektų elgsenai modifikuoti. Sužinokite apie validavimą, virtualizavimą, sekimą ir kitas pažangias technikas su kodo pavyzdžiais.
JavaScript Proxy šablonai: objektų elgsenos modifikavimo įvaldymas
JavaScript Proxy objektas suteikia galingą mechanizmą, leidžiantį perimti ir pritaikyti fundamentalias objektų operacijas. Ši galimybė atveria duris į platų projektavimo šablonų ir pažangių technikų spektrą, skirtą objektų elgsenai valdyti. Šiame išsamiame vadove nagrinėjami įvairūs Proxy šablonai, iliustruojant jų naudojimą praktiniais kodo pavyzdžiais.
Kas yra JavaScript Proxy?
Proxy objektas apgaubia kitą objektą (tikslinį objektą) ir perima jo operacijas. Šios operacijos, žinomos kaip spąstai (angl. traps), apima savybių paiešką, priskyrimą, išvardijimą ir funkcijų iškvietimą. Proxy leidžia jums apibrėžti pasirinktinę logiką, kuri bus vykdoma prieš, po arba vietoj šių operacijų. Pagrindinė Proxy koncepcija yra susijusi su „metaprogramavimu“, kuris leidžia manipuliuoti pačios JavaScript kalbos elgsena.
Pagrindinė Proxy kūrimo sintaksė yra:
const proxy = new Proxy(target, handler);
- target: Originalus objektas, kuriam norite sukurti proxy.
- handler: Objektas, kuriame yra metodai (spąstai), apibrėžiantys, kaip Proxy perims operacijas su tiksliniu objektu.
Dažniausiai naudojami Proxy spąstai
Handler objektas gali apibrėžti kelis spąstus. Štai keletas dažniausiai naudojamų:
- get(target, property, receiver): Perima savybės prieigą (pvz.,
obj.property
). - set(target, property, value, receiver): Perima savybės priskyrimą (pvz.,
obj.property = value
). - has(target, property): Perima
in
operatorių (pvz.,'property' in obj
). - deleteProperty(target, property): Perima
delete
operatorių (pvz.,delete obj.property
). - apply(target, thisArg, argumentsList): Perima funkcijų iškvietimus (kai tikslinis objektas yra funkcija).
- construct(target, argumentsList, newTarget): Perima
new
operatorių (kai tikslinis objektas yra konstruktoriaus funkcija). - getPrototypeOf(target): Perima
Object.getPrototypeOf()
iškvietimus. - setPrototypeOf(target, prototype): Perima
Object.setPrototypeOf()
iškvietimus. - isExtensible(target): Perima
Object.isExtensible()
iškvietimus. - preventExtensions(target): Perima
Object.preventExtensions()
iškvietimus. - getOwnPropertyDescriptor(target, property): Perima
Object.getOwnPropertyDescriptor()
iškvietimus. - defineProperty(target, property, descriptor): Perima
Object.defineProperty()
iškvietimus. - ownKeys(target): Perima
Object.getOwnPropertyNames()
irObject.getOwnPropertySymbols()
iškvietimus.
Proxy šablonai ir jų panaudojimo atvejai
Panagrinėkime keletą įprastų Proxy šablonų ir kaip juos galima pritaikyti realaus pasaulio scenarijuose:
1. Validavimas
Validavimo šablonas naudoja Proxy, kad įgyvendintų apribojimus savybių priskyrimams. Tai naudinga užtikrinant duomenų vientisumą.
const validator = {
set: function(obj, prop, value) {
if (prop === 'age') {
if (!Number.isInteger(value)) {
throw new TypeError('Amžius nėra sveikasis skaičius');
}
if (value < 0) {
throw new RangeError('Amžius turi būti neneigiamas sveikasis skaičius');
}
}
// Numatytasis elgesys reikšmei išsaugoti
obj[prop] = value;
// Nurodyti sėkmę
return true;
}
};
let person = {};
let proxy = new Proxy(person, validator);
proxy.age = 25; // Teisinga
console.log(proxy.age); // Išvestis: 25
try {
proxy.age = 'jaunas'; // Išmeta TypeError
} catch (e) {
console.log(e); // Išvestis: TypeError: Amžius nėra sveikasis skaičius
}
try {
proxy.age = -10; // Išmeta RangeError
} catch (e) {
console.log(e); // Išvestis: RangeError: Amžius turi būti neneigiamas sveikasis skaičius
}
Pavyzdys: Įsivaizduokite e. prekybos platformą, kurioje reikia patvirtinti vartotojo duomenis. Proxy gali priverstinai taikyti taisykles amžiui, el. pašto formatui, slaptažodžio stiprumui ir kitiems laukams, užkertant kelią neteisingų duomenų saugojimui.
2. Virtualizavimas (atidėtas įkėlimas)
Virtualizavimas, taip pat žinomas kaip atidėtas įkėlimas (angl. lazy loading), atideda brangių išteklių įkėlimą, kol jų iš tikrųjų prireikia. Proxy gali veikti kaip tikrojo objekto pakaitalas, įkeliantis jį tik tada, kai pasiekiama savybė.
const expensiveData = {
load: function() {
console.log('Įkeliami brangūs duomenys...');
// Imituojama daug laiko reikalaujanti operacija (pvz., duomenų gavimas iš duomenų bazės)
return new Promise(resolve => {
setTimeout(() => {
resolve({
data: 'Tai yra brangūs duomenys'
});
}, 2000);
});
}
};
const lazyLoadHandler = {
get: function(target, prop) {
if (prop === 'data') {
console.log('Prieinama prie duomenų, įkeliama, jei reikia...');
return target.load().then(result => {
target.data = result.data; // Išsaugomi įkelti duomenys
return result.data;
});
} else {
return target[prop];
}
}
};
const lazyData = new Proxy(expensiveData, lazyLoadHandler);
console.log('Pradinė prieiga...');
lazyData.data.then(data => {
console.log('Duomenys:', data); // Išvestis: Duomenys: Tai yra brangūs duomenys
});
console.log('Vėlesnė prieiga...');
lazyData.data.then(data => {
console.log('Duomenys:', data); // Išvestis: Duomenys: Tai yra brangūs duomenys (įkelta iš podėlio)
});
Pavyzdys: Įsivaizduokite didelę socialinių tinklų platformą su vartotojų profiliais, kuriuose yra daugybė detalių ir susijusios medijos. Visų profilio duomenų įkėlimas iškart gali būti neefektyvus. Virtualizavimas su Proxy leidžia pirmiausia įkelti pagrindinę profilio informaciją, o papildomas detales ar medijos turinį įkelti tik tada, kai vartotojas pereina į tas skiltis.
3. Registravimas ir sekimas
Proxy gali būti naudojami savybių prieigai ir modifikacijoms sekti. Tai vertinga derinant kodą, atliekant auditą ir stebint našumą.
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} į ${value}`);
target[prop] = value;
return true;
}
};
let obj = { name: 'Alice' };
let proxy = new Proxy(obj, logHandler);
console.log(proxy.name); // Išvestis: GET name, Alice
proxy.age = 30; // Išvestis: SET age į 30
Pavyzdys: Bendradarbiavimo dokumentų redagavimo programoje Proxy gali sekti kiekvieną dokumento turinio pakeitimą. Tai leidžia sukurti audito seką, įgalinti anuliavimo/grąžinimo (undo/redo) funkcionalumą ir suteikti įžvalgų apie vartotojų indėlį.
4. Tik skaitymui skirti rodiniai
Proxy gali sukurti tik skaitymui skirtus objektų rodinius, užkertant kelią atsitiktiniams pakeitimams. Tai naudinga norint apsaugoti jautrius duomenis.
const readOnlyHandler = {
set: function(target, prop, value) {
console.error(`Negalima nustatyti savybės ${prop}: objektas yra tik skaitomas`);
return false; // Nurodo, kad nustatymo operacija nepavyko
},
deleteProperty: function(target, prop) {
console.error(`Negalima ištrinti savybės ${prop}: objektas yra tik skaitomas`);
return false; // Nurodo, kad trynimo operacija nepavyko
}
};
let data = { name: 'Bob', age: 40 };
let readOnlyData = new Proxy(data, readOnlyHandler);
try {
readOnlyData.age = 41; // Išmeta klaidą
} catch (e) {
console.log(e); // Klaida neišmetama, nes 'set' spąstai grąžina false.
}
try {
delete readOnlyData.name; // Išmeta klaidą
} catch (e) {
console.log(e); // Klaida neišmetama, nes 'deleteProperty' spąstai grąžina false.
}
console.log(data.age); // Išvestis: 40 (nepakeista)
Pavyzdys: Apsvarstykite finansų sistemą, kurioje kai kurie vartotojai turi tik skaitymo prieigą prie sąskaitos informacijos. Proxy gali būti naudojamas siekiant užkirsti kelią šiems vartotojams modifikuoti sąskaitos likučius ar kitus svarbius duomenis.
5. Numatytosios reikšmės
Proxy gali pateikti numatytąsias reikšmes trūkstamoms savybėms. Tai supaprastina kodą ir leidžia išvengti null/undefined patikrinimų.
const defaultValuesHandler = {
get: function(target, prop, receiver) {
if (!(prop in target)) {
console.log(`Savybė ${prop} nerasta, grąžinama numatytoji reikšmė.`);
return 'Numatytoji reikšmė'; // Arba bet kokia kita tinkama numatytoji reikšmė
}
return Reflect.get(target, prop, receiver);
}
};
let config = { apiUrl: 'https://api.example.com' };
let configWithDefaults = new Proxy(config, defaultValuesHandler);
console.log(configWithDefaults.apiUrl); // Išvestis: https://api.example.com
console.log(configWithDefaults.timeout); // Išvestis: Savybė timeout nerasta, grąžinama numatytoji reikšmė. Numatytosis reikšmė
Pavyzdys: Konfigūracijos valdymo sistemoje Proxy gali pateikti numatytąsias reikšmes trūkstamiems nustatymams. Pavyzdžiui, jei konfigūracijos faile nenurodytas duomenų bazės prisijungimo laiko limitas, Proxy gali grąžinti iš anksto nustatytą numatytąją reikšmę.
6. Metaduomenys ir anotacijos
Proxy gali pridėti metaduomenis ar anotacijas prie objektų, suteikdami papildomos informacijos nekeičiant originalaus objekto.
const metadataHandler = {
get: function(target, prop, receiver) {
if (prop === '__metadata__') {
return { description: 'Tai yra objekto metaduomenys' };
}
return Reflect.get(target, prop, receiver);
}
};
let article = { title: 'Įvadas į Proxy', content: '...' };
let articleWithMetadata = new Proxy(article, metadataHandler);
console.log(articleWithMetadata.title); // Išvestis: Įvadas į Proxy
console.log(articleWithMetadata.__metadata__.description); // Išvestis: Tai yra objekto metaduomenys
Pavyzdys: Turinio valdymo sistemoje Proxy gali pridėti metaduomenis prie straipsnių, tokius kaip autoriaus informacija, publikavimo data ir raktažodžiai. Šie metaduomenys gali būti naudojami turinio paieškai, filtravimui ir kategorizavimui.
7. Funkcijų perėmimas
Proxy gali perimti funkcijų iškvietimus, leidžiant jums pridėti registravimo, validavimo ar kitą išankstinio arba vėlesnio apdorojimo logiką.
const functionInterceptor = {
apply: function(target, thisArg, argumentsList) {
console.log('Kviečiama funkcija su argumentais:', argumentsList);
const result = target.apply(thisArg, argumentsList);
console.log('Funkcija grąžino:', result);
return result;
}
};
function add(a, b) {
return a + b;
}
let proxiedAdd = new Proxy(add, functionInterceptor);
let sum = proxiedAdd(5, 3); // Išvestis: Kviečiama funkcija su argumentais: [5, 3], Funkcija grąžino: 8
console.log(sum); // Išvestis: 8
Pavyzdys: Bankininkystės programoje Proxy gali perimti iškvietimus į transakcijų funkcijas, registruodamas kiekvieną transakciją ir atlikdamas sukčiavimo aptikimo patikrinimus prieš įvykdant transakciją.
8. Konstruktorių perėmimas
Proxy gali perimti konstruktorių iškvietimus, leidžiant jums pritaikyti objektų kūrimą.
const constructorInterceptor = {
construct: function(target, argumentsList, newTarget) {
console.log('Kuriamas naujas egzempliorius', target.name, 'su argumentais:', argumentsList);
const obj = new target(...argumentsList);
console.log('Sukurtas naujas egzempliorius:', 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); // Išvestis: Kuriamas naujas egzempliorius Person su argumentais: ['Alice', 28], Sukurtas naujas egzempliorius: Person { name: 'Alice', age: 28 }
console.log(person);
Pavyzdys: Žaidimų kūrimo karkase Proxy gali perimti žaidimo objektų kūrimą, automatiškai priskirdamas unikalius ID, pridėdamas numatytuosius komponentus ir registruodamas juos žaidimo variklyje.
Pažangesni aspektai
- Našumas: Nors Proxy suteikia lankstumo, jie gali sukelti našumo praradimą. Svarbu atlikti kodo testavimą ir profiliavimą, siekiant užtikrinti, kad Proxy naudojimo nauda viršytų našumo kaštus, ypač našumui kritinėse programose.
- Suderinamumas: Proxy yra palyginti naujas JavaScript priedas, todėl senesnės naršyklės gali jų nepalaikyti. Naudokite funkcijų aptikimą arba polifilus, kad užtikrintumėte suderinamumą su senesnėmis aplinkomis.
- Atšaukiami Proxy:
Proxy.revocable()
metodas sukuria Proxy, kurį galima atšaukti. Atšaukus Proxy, jokie tolesni veiksmai nebus perimami. Tai gali būti naudinga saugumo ar išteklių valdymo tikslais. - Reflect API: Reflect API suteikia metodus, leidžiančius atlikti numatytąjį Proxy spąstų elgesį. Naudojant
Reflect
užtikrinama, kad jūsų Proxy kodas veiks nuosekliai su kalbos specifikacija.
Išvada
JavaScript Proxy suteikia galingą ir universalų mechanizmą, skirtą objektų elgsenai pritaikyti. Įvaldę įvairius Proxy šablonus, galite rašyti tvirtesnį, lengviau prižiūrimą ir efektyvesnį kodą. Nesvarbu, ar įgyvendinate validavimą, virtualizavimą, sekimą ar kitas pažangias technikas, Proxy siūlo lankstų sprendimą, kaip valdyti objektų prieigą ir manipuliavimą. Visada atsižvelkite į našumo pasekmes ir užtikrinkite suderinamumą su savo tikslinėmis aplinkomis. Proxy yra pagrindinis šiuolaikinio JavaScript programuotojo arsenalo įrankis, leidžiantis taikyti galingas metaprogramavimo technikas.
Tolesnis tyrinėjimas
- Mozilla Developer Network (MDN): JavaScript Proxy
- „Exploring JavaScript Proxies“: „Smashing Magazine“ straipsnis