Proxy va Reflect
Muammodan boshlaylik
Ob'ektga xususiyat yozilganda avtomatik tekshirish kerak. Yoki o'qilganda — kim o'qiganini log qilish. Yoki mavjud bo'lmagan xususiyatga murojaat qilinganda xato o'rniga standart qiymat berish.
Oddiy ob'ektda buni qilib bo'lmaydi. Getter va setter — faqat oldindan nomi ma'lum xususiyatlar uchun. Dinamik, istalgan xususiyat uchun — setter yoza olmaysiz.
Proxy — ob'ektga har qanday murojaatni "ushlab olish" imkoniyatini beradi. O'qish, yozish, o'chirish — barchasini nazorat qilish.
Proxy nima?
Proxy — ob'ektni "o'rab oluvchi" qatlam. Ob'ektga har murojaat — avval Proxy orqali o'tadi. Proxy qaror qiladi: murojaatni o'tkazishmi, to'xtatishmi, o'zgartirishmi.
Hayotiy o'xshatma
Kotib (referent). Direktorga murojaat qilmoqchisiz. Lekin direktorga to'g'ridan bora olmaysiz — kotib orqali. Kotib qaror qiladi: "direktorn band, keyinroq keling" yoki "kiring", yoki "bu savol boshqa bo'limga tegishli — u yerga boring". Kotib — Proxy. Direktor — haqiqiy ob'ekt.
Proxy yaratish
const haqiqiy_ob = { ism: "Jasur", yosh: 25 };
const proksi = new Proxy(haqiqiy_ob, {
// "trap" lar — ushlash funksiyalari
});
Ikkinchi argument — handler — trap lar to'plami. Trap — murojaat ushlanganida ishlaydigan funksiya.
get trap — o'qishni ushlab olish
Xususiyat o'qilganda ishlaydi:
const foydalanuvchi = { ism: "Jasur", yosh: 25 };
const proksi = new Proxy(foydalanuvchi, {
get(target, xususiyat) {
// ↑ ↑
// haqiqiy ob'ekt xususiyat nomi
console.log(`"${xususiyat}" o'qildi`);
// Xususiyat mavjud bo'lsa — haqiqiy qiymat
// Mavjud bo'lmasa — standart qiymat
return xususiyat in target
? target[xususiyat]
: `"${xususiyat}" mavjud emas`;
}
});
console.log(proksi.ism); // ← "ism" o'qildi → "Jasur"
console.log(proksi.telefon); // ← "telefon" o'qildi → '"telefon" mavjud emas'
Nima uchun bu foydali?
Odatda mavjud bo'lmagan xususiyat undefined qaytaradi — xato ko'rinmaydi, muammo kechroq chiqadi. Proxy bilan — darhol aniq xabar.
set trap — yozishni ushlab olish
Xususiyatga qiymat berilganda ishlaydi:
const foydalanuvchi = {};
const tekshiruvchi = new Proxy(foydalanuvchi, {
set(target, xususiyat, qiymat) {
// ↑ ↑
// xususiyat nomi yangi qiymat
if (xususiyat === "yosh") {
if (typeof qiymat !== "number") {
throw new TypeError("Yosh son bo'lishi kerak");
}
if (qiymat < 0 || qiymat > 150) {
throw new RangeError("Yosh 0 dan 150 gacha bo'lishi kerak");
}
}
target[xususiyat] = qiymat; // ← validatsiyadan o'tdi — yozish
return true; // ← set trap true qaytarishi shart
}
});
tekshiruvchi.ism = "Jasur"; // ✅
tekshiruvchi.yosh = 25; // ✅
tekshiruvchi.yosh = -5; // ❌ RangeError: Yosh 0 dan 150 gacha bo'lishi kerak
tekshiruvchi.yosh = "katta"; // ❌ TypeError: Yosh son bo'lishi kerak
Bu — setter ning kuchaytirilgan versiyasi. Oldindan xususiyat nomini bilmasdan ham, har qanday xususiyatni tekshirish mumkin.
has trap — in operatorini ushlab olish
const doira = new Proxy({ radius: 5 }, {
has(target, xususiyat) {
// "in" operatori chaqirilganda
if (xususiyat === "area") return true; // ← "area" borday ko'rsatish
return xususiyat in target;
},
get(target, xususiyat) {
if (xususiyat === "area") {
return Math.PI * target.radius ** 2;
}
return target[xususiyat];
}
});
console.log("area" in doira); // true ← has trap
console.log(doira.area); // 78.53... ← get trap
deleteProperty trap — o'chirishni ushlab olish
const himoyalangan = new Proxy({ maxfiy: "sir", ommaviy: "ma'lumot" }, {
deleteProperty(target, xususiyat) {
if (xususiyat === "maxfiy") {
throw new Error("Maxfiy xususiyatni o'chirib bo'lmaydi!");
}
delete target[xususiyat];
return true;
}
});
delete himoyalangan.ommaviy; // ✅
delete himoyalangan.maxfiy; // ❌ Error: Maxfiy xususiyatni o'chirib bo'lmaydi!
Reflect — trap ichida xavfsiz murojaat
Trap ichida haqiqiy ob'ektga murojaat qilishda target[xususiyat] o'rniga Reflect ishlatish tavsiya etiladi:
const proksi = new Proxy(ob, {
get(target, xususiyat, receiver) {
console.log(`"${xususiyat}" o'qildi`);
return Reflect.get(target, xususiyat, receiver); // ← to'g'ri murojaat
},
set(target, xususiyat, qiymat, receiver) {
console.log(`"${xususiyat}" = ${qiymat}`);
return Reflect.set(target, xususiyat, qiymat, receiver);
}
});
Reflect — JavaScript ning ichki operatsiyalarini funksiya sifatida. Har bir Proxy trap uchun mos Reflect metodi bor.
Nima uchun target[x] o'rniga Reflect.get?
receiver — meros (inheritance) zanjirida to'g'ri this ni saqlash uchun. Oddiy target[x] ba'zi holatlarda this ni noto'g'ri ko'rsatishi mumkin. Reflect.get(target, x, receiver) bu muammoni hal qiladi.
Amaliy misol — kuzatuvchi (observable)
function kuzatuvchiYarat(ob, callback) {
return new Proxy(ob, {
set(target, xususiyat, qiymat) {
const eski = target[xususiyat];
Reflect.set(target, xususiyat, qiymat);
if (eski !== qiymat) {
callback(xususiyat, eski, qiymat); // ← o'zgardi!
}
return true;
}
});
}
const holat = kuzatuvchiYarat(
{ soni: 0, matn: "" },
function(xususiyat, eski, yangi) {
console.log(`${xususiyat}: ${eski} → ${yangi}`);
}
);
holat.soni = 5; // "soni: 0 → 5"
holat.matn = "salom"; // "matn: → salom"
holat.soni = 5; // ← o'zgarmadi, callback ishlamadi
Tez-tez uchraydigan xatolar
set trap da true qaytarmaslik:
// ❌ — strict modeda TypeError
set(target, prop, value) {
target[prop] = value;
// return yo'q → undefined → false → xato
}
// ✅
set(target, prop, value) {
target[prop] = value;
return true; // ← muvaffaqiyatli yozildi
}
Proksi orqali emas, haqiqiy ob'ektga murojaat:
const asl = { son: 0 };
const proksi = new Proxy(asl, { set(t, p, v) { console.log("trap"); t[p]=v; return true; } });
proksi.son = 5; // "trap" — proksi orqali
asl.son = 10; // ← trap ishlamadi — to'g'ridan asl ob'ektga
Proksi orqali ishlash kerak bo'lsa — faqat proksi ishlatiladi, asl ob'ektga to'g'ridan murojaat qilinmaydi.
Xulosa
Proxy — ob'ektga har murojaatni ushlab oluvchi qatlam. Kotib o'xshatmasi: har murojaat kotib orqali.
Traplar: get (o'qish), set (yozish), has (in), deleteProperty (o'chirish) va boshqalar.
Reflect — Proxy trap ichida haqiqiy ob'ektga to'g'ri murojaat uchun.
Ishlatilishi: validatsiya, logging, himoya, reaktiv holat (Vue.js ichki mexanizmi aynan Proxy da qurilgan).
Keyingi dars
61-dars: WeakMap va WeakSet — nima uchun kerak