الگوهای پیشرفته پروکسی جاوااسکریپت را برای رهگیری، اعتبارسنجی و رفتار پویا در اشیاء کاوش کنید. بیاموزید چگونه کیفیت کد، امنیت و قابلیت نگهداری را با مثالهای عملی بهبود بخشید.
الگوهای پروکسی در جاوااسکریپت: رهگیری و اعتبارسنجی پیشرفته اشیاء
شیء پروکسی جاوااسکریپت یک ویژگی قدرتمند است که به شما امکان میدهد عملیات اصلی اشیاء را رهگیری و سفارشیسازی کنید. این شیء تکنیکهای برنامهنویسی متا پیشرفته را فعال میکند و کنترل بیشتری بر رفتار اشیاء و امکاناتی برای الگوهای طراحی پیچیده فراهم میآورد. این مقاله الگوهای مختلف پروکسی را بررسی کرده و موارد استفاده آنها را در اعتبارسنجی، رهگیری و تغییر رفتار پویا نشان میدهد. ما به مثالهای عملی خواهیم پرداخت تا نشان دهیم چگونه پروکسیها میتوانند کیفیت کد، امنیت و قابلیت نگهداری را در پروژههای جاوااسکریپت شما افزایش دهند.
درک پروکسی جاوااسکریپت
در هسته خود، یک شیء پروکسی، شیء دیگری (هدف) را در بر میگیرد و عملیات انجام شده بر روی آن هدف را رهگیری میکند. این رهگیریها توسط تلهها (traps) مدیریت میشوند که متدهایی هستند که رفتار سفارشی را برای عملیات خاصی مانند دریافت یک ویژگی، تنظیم یک ویژگی یا فراخوانی یک تابع تعریف میکنند. API پروکسی مکانیزمی انعطافپذیر و توسعهپذیر برای تغییر رفتار پیشفرض اشیاء فراهم میکند.
مفاهیم کلیدی
- هدف (Target): شیء اصلی که پروکسی آن را در بر میگیرد.
- هندلر (Handler): شیئی که حاوی متدهای تله است. هر تله مربوط به یک عملیات خاص است.
- تلهها (Traps): متدهایی درون هندلر که عملیات اشیاء را رهگیری و سفارشیسازی میکنند. تلههای رایج شامل
get،set،applyوconstructهستند.
ایجاد یک پروکسی
برای ایجاد یک پروکسی، از سازنده Proxy استفاده میکنید و شیء هدف و شیء هندلر را به عنوان آرگومانها ارسال میکنید:
const target = {};
const handler = {
get: function(target, property, receiver) {
console.log(`Getting property: ${property}`);
return Reflect.get(target, property, receiver);
}
};
const proxy = new Proxy(target, handler);
proxy.name = "John"; // Logs: Getting property: name
console.log(proxy.name); // Logs: Getting property: name, then John
تلههای رایج پروکسی
پروکسیها مجموعهای از تلهها را برای رهگیری عملیات مختلف ارائه میدهند. در اینجا برخی از رایجترین تلهها آورده شدهاند:
get(target, property, receiver): دسترسی به ویژگی را رهگیری میکند.set(target, property, value, receiver): تخصیص ویژگی را رهگیری میکند.has(target, property): عملگرinرا رهگیری میکند.deleteProperty(target, property): عملگرdeleteرا رهگیری میکند.apply(target, thisArg, argumentsList): فراخوانی تابع را رهگیری میکند.construct(target, argumentsList, newTarget): عملگرnewرا رهگیری میکند.getPrototypeOf(target): متدObject.getPrototypeOf()را رهگیری میکند.setPrototypeOf(target, prototype): متدObject.setPrototypeOf()را رهگیری میکند.isExtensible(target): متدObject.isExtensible()را رهگیری میکند.preventExtensions(target): متدObject.preventExtensions()را رهگیری میکند.getOwnPropertyDescriptor(target, property): متدObject.getOwnPropertyDescriptor()را رهگیری میکند.defineProperty(target, property, descriptor): متدObject.defineProperty()را رهگیری میکند.ownKeys(target): متدهایObject.getOwnPropertyNames()وObject.getOwnPropertySymbols()را رهگیری میکند.
الگوهای پروکسی
اکنون، بیایید برخی از الگوهای عملی پروکسی و کاربردهای آنها را بررسی کنیم:
1. پروکسی اعتبارسنجی (Validation Proxy)
یک پروکسی اعتبارسنجی محدودیتهایی را بر روی تخصیص ویژگیها اعمال میکند. این پروکسی تله set را رهگیری میکند تا مقدار جدید را قبل از اینکه اجازه دهد تخصیص ادامه یابد، اعتبارسنجی کند.
مثال: اعتبارسنجی ورودی کاربر در یک فرم.
const user = {};
const validator = {
set: function(target, property, value) {
if (property === 'age') {
if (!Number.isInteger(value) || value < 0 || value > 120) {
throw new Error('Invalid age. Age must be an integer between 0 and 120.');
}
}
target[property] = value;
return true; // Indicate success
}
};
const proxy = new Proxy(user, validator);
proxy.name = 'Alice';
proxy.age = 30;
console.log(user);
try {
proxy.age = 'invalid'; // Throws an error
} catch (error) {
console.error(error.message);
}
در این مثال، تله set بررسی میکند که آیا ویژگی age یک عدد صحیح بین 0 تا 120 است یا خیر. اگر اعتبارسنجی شکست بخورد، یک خطا پرتاب میشود و از تخصیص مقدار نامعتبر جلوگیری میکند.
مثال جهانی: این الگوی اعتبارسنجی برای تضمین یکپارچگی دادهها در برنامههای جهانی که ورودی کاربر ممکن است از منابع و فرهنگهای متنوعی باشد، ضروری است. به عنوان مثال، اعتبارسنجی کدهای پستی میتواند بین کشورها تفاوت قابل توجهی داشته باشد. یک پروکسی اعتبارسنجی را میتوان برای پشتیبانی از قوانین اعتبارسنجی مختلف بر اساس موقعیت مکانی کاربر تطبیق داد.
const address = {};
const addressValidator = {
set: function(target, property, value) {
if (property === 'postalCode') {
// Example: Assuming a simple US postal code validation
if (!/^[0-9]{5}(?:-[0-9]{4})?$/.test(value)) {
throw new Error('Invalid US postal code.');
}
}
target[property] = value;
return true;
}
};
const addressProxy = new Proxy(address, addressValidator);
addressProxy.postalCode = "12345-6789"; // Valid
try {
addressProxy.postalCode = "abcde"; // Invalid
} catch(e) {
console.log(e);
}
// For a more international application, you'd use a more sophisticated validation library
// that could validate postal codes based on the user's country.
2. پروکسی ثبت رویداد (Logging Proxy)
یک پروکسی ثبت رویداد، دسترسی و تخصیص ویژگیها را برای ثبت این عملیات رهگیری میکند. این برای اشکالزدایی و ممیزی مفید است.
مثال: ثبت دسترسی و تغییر ویژگی.
const data = {
value: 10
};
const logger = {
get: function(target, property) {
console.log(`Getting property: ${property}`);
return target[property];
},
set: function(target, property, value) {
console.log(`Setting property: ${property} to ${value}`);
target[property] = value;
return true;
}
};
const proxy = new Proxy(data, logger);
console.log(proxy.value); // Logs: Getting property: value, then 10
proxy.value = 20; // Logs: Setting property: value to 20
تلههای get و set ویژگی در حال دسترسی یا تغییر را ثبت میکنند و ردی از تعاملات شیء را ارائه میدهند.
مثال جهانی: در یک شرکت چندملیتی، میتوان از پروکسیهای ثبت رویداد برای ممیزی دسترسی به دادهها و تغییراتی که توسط کارمندان در مکانهای مختلف انجام میشود، استفاده کرد. این برای اهداف انطباق و امنیت حیاتی است. ممکن است نیاز باشد مناطق زمانی در اطلاعات ثبت رویداد در نظر گرفته شوند.
const employeeData = {
name: "John Doe",
salary: 50000
};
const auditLogger = {
get: function(target, property) {
const timestamp = new Date().toISOString();
console.log(`${timestamp} - [GET] Accessing property: ${property}`);
return target[property];
},
set: function(target, property, value) {
const timestamp = new Date().toISOString();
console.log(`${timestamp} - [SET] Setting property: ${property} to ${value}`);
target[property] = value;
return true;
}
};
const proxiedEmployee = new Proxy(employeeData, auditLogger);
proxiedEmployee.name; // Logs timestamp and access to 'name'
proxiedEmployee.salary = 60000; // Logs timestamp and modification of 'salary'
3. پروکسی فقط خواندنی (Read-Only Proxy)
یک پروکسی فقط خواندنی از تخصیص ویژگی جلوگیری میکند. این پروکسی تله set را رهگیری کرده و در صورت تلاش برای تغییر یک ویژگی، خطا پرتاب میکند.
مثال: غیرقابل تغییر کردن یک شیء.
const config = {
apiUrl: 'https://api.example.com'
};
const readOnly = {
set: function(target, property, value) {
throw new Error(`Cannot set property: ${property}. Object is read-only.`);
}
};
const proxy = new Proxy(config, readOnly);
console.log(proxy.apiUrl);
try {
proxy.apiUrl = 'https://newapi.example.com'; // Throws an error
} catch (error) {
console.error(error.message);
}
هر تلاشی برای تنظیم یک ویژگی بر روی پروکسی منجر به خطا میشود و تضمین میکند که شیء غیرقابل تغییر باقی میماند.
مثال جهانی: این الگو برای محافظت از فایلهای پیکربندی که نباید در زمان اجرا تغییر داده شوند، به ویژه در برنامههای توزیعشده جهانی، مفید است. تغییر تصادفی پیکربندی در یک منطقه میتواند کل سیستم را تحت تأثیر قرار دهد.
const globalSettings = {
defaultLanguage: "en",
currency: "USD",
timeZone: "UTC"
};
const immutableHandler = {
set: function(target, property, value) {
throw new Error(`Cannot modify read-only property: ${property}`);
}
};
const immutableSettings = new Proxy(globalSettings, immutableHandler);
console.log(immutableSettings.defaultLanguage); // outputs 'en'
// Attempting to change a value will throw an error
// immutableSettings.defaultLanguage = "fr"; // throws Error: Cannot modify read-only property: defaultLanguage
4. پروکسی مجازی (Virtual Proxy)
یک پروکسی مجازی دسترسی به منبعی را کنترل میکند که ایجاد یا بازیابی آن ممکن است پرهزینه باشد. این پروکسی میتواند ایجاد منبع را تا زمانی که واقعاً مورد نیاز باشد، به تأخیر بیندازد.
مثال: بارگذاری تنبل یک تصویر.
const image = {
display: function() {
console.log('Displaying image');
}
};
const virtualProxy = {
get: function(target, property) {
if (property === 'display') {
console.log('Creating image...');
const realImage = {
display: function() {
console.log('Displaying real image');
}
};
target.display = realImage.display;
return realImage.display;
}
return target[property];
}
};
const proxy = new Proxy(image, virtualProxy);
// The image is not created until display is called.
proxy.display(); // Logs: Creating image..., then Displaying real image
شیء تصویر واقعی تنها زمانی ایجاد میشود که متد display فراخوانی شود، که از مصرف منابع غیرضروری جلوگیری میکند.
مثال جهانی: یک وبسایت تجارت الکترونیک جهانی را در نظر بگیرید که تصاویر محصولات را ارائه میدهد. با استفاده از یک پروکسی مجازی، تصاویر فقط زمانی بارگذاری میشوند که برای کاربر قابل مشاهده باشند، که مصرف پهنای باند را بهینه کرده و زمان بارگذاری صفحه را بهبود میبخشد، به ویژه برای کاربرانی با اتصالات اینترنتی کند در مناطق مختلف.
const product = {
loadImage: function() {
console.log("Loading high-resolution image...");
// Simulate loading a large image
setTimeout(() => {
console.log("Image loaded");
this.displayImage();
}, 2000);
},
displayImage: function() {
console.log("Displaying the image");
}
};
const lazyLoadProxy = {
get: function(target, property) {
if (property === "displayImage") {
// Instead of loading immediately, delay the loading
console.log("Request to display image received. Loading...");
target.loadImage();
return function() { /* Intentionally Empty */ }; // Return empty function to prevent immediate execution
}
return target[property];
}
};
const proxiedProduct = new Proxy(product, lazyLoadProxy);
// Call displayImage triggers the lazy loading process
proxiedProduct.displayImage();
5. پروکسی قابل لغو (Revocable Proxy)
یک پروکسی قابل لغو به شما امکان میدهد در هر زمان پروکسی را لغو کنید و آن را غیرقابل استفاده سازید. این برای سناریوهای حساس به امنیت که نیاز به کنترل دسترسی به یک شیء دارید، مفید است.
مثال: اعطای دسترسی موقت به یک منبع.
const target = {
secret: 'This is a secret'
};
const handler = {
get: function(target, property) {
console.log('Accessing secret property');
return target[property];
}
};
const { proxy, revoke } = Proxy.revocable(target, handler);
console.log(proxy.secret); // Logs: Accessing secret property, then This is a secret
revoke();
try {
console.log(proxy.secret); // Throws a TypeError
} catch (error) {
console.error(error.message); // Logs: Cannot perform 'get' on a proxy that has been revoked
}
متد Proxy.revocable() یک پروکسی قابل لغو ایجاد میکند. فراخوانی تابع revoke() پروکسی را غیرقابل استفاده میکند و از دسترسی بیشتر به شیء هدف جلوگیری میکند.
مثال جهانی: در یک سیستم توزیعشده جهانی، ممکن است از یک پروکسی قابل لغو برای اعطای دسترسی موقت به دادههای حساس به یک سرویس در حال اجرا در یک منطقه خاص استفاده کنید. پس از مدت زمان مشخصی، پروکسی میتواند لغو شود تا از دسترسی غیرمجاز جلوگیری شود.
const sensitiveData = {
apiKey: "SUPER_SECRET_KEY"
};
const handler = {
get: function(target, property) {
console.log("Accessing sensitive data");
return target[property];
}
};
const { proxy: dataProxy, revoke: revokeAccess } = Proxy.revocable(sensitiveData, handler);
// Allow access for 5 seconds
setTimeout(() => {
revokeAccess();
console.log("Access revoked");
}, 5000);
// Attempt to access data
console.log(dataProxy.apiKey); // Logs the API Key
// After 5 seconds, this will throw an error
setTimeout(() => {
try {
console.log(dataProxy.apiKey); // Throws: TypeError: Cannot perform 'get' on a proxy that has been revoked
} catch (error) {
console.error(error);
}
}, 6000);
6. پروکسی تبدیل نوع (Type Conversion Proxy)
یک پروکسی تبدیل نوع، دسترسی به ویژگی را رهگیری میکند تا مقدار بازگشتی را به طور خودکار به یک نوع خاص تبدیل کند. این میتواند برای کار با دادههایی از منابع مختلف که ممکن است انواع ناسازگار داشته باشند، مفید باشد.
مثال: تبدیل مقادیر رشتهای به اعداد.
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); // Logs: 11.99 (number)
console.log(proxy.quantity * 2); // Logs: 10 (number)
تله get بررسی میکند که آیا مقدار ویژگی یک رشته است که میتواند به عدد تبدیل شود. اگر چنین باشد، مقدار را قبل از بازگرداندن به عدد تبدیل میکند.
مثال جهانی: هنگام کار با دادههایی که از APIها با قراردادهای قالببندی متفاوت (مانند فرمتهای تاریخ یا نمادهای ارزی متفاوت) میآیند، یک پروکسی تبدیل نوع میتواند یکپارچگی دادهها را در سراسر برنامه شما، صرف نظر از منبع، تضمین کند. به عنوان مثال، مدیریت فرمتهای تاریخ مختلف و تبدیل همه آنها به فرمت ISO 8601.
const apiData = {
dateUS: "12/31/2023",
dateEU: "31/12/2023"
};
const dateFormatConverter = {
get: function(target, property) {
let value = target[property];
if (property.startsWith("date")) {
// Attempt to convert both US and EU date formats to ISO 8601
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); // Outputs: 2023-12-31
console.log(proxiedApiData.dateEU); // Outputs: 2023-12-31
بهترین روشها برای استفاده از پروکسیها
- از پروکسیها با دقت استفاده کنید: پروکسیها میتوانند به پیچیدگی کد شما بیفزایند. فقط زمانی از آنها استفاده کنید که مزایای قابل توجهی مانند اعتبارسنجی بهبود یافته، ثبت رویداد یا کنترل رفتار شیء را فراهم کنند.
- عملکرد را در نظر بگیرید: تلههای پروکسی میتوانند سربار ایجاد کنند. کد خود را پروفایل کنید تا اطمینان حاصل شود که پروکسیها تأثیر منفی بر عملکرد ندارند، به ویژه در بخشهای حساس به عملکرد.
- خطاها را با ظرافت مدیریت کنید: اطمینان حاصل کنید که متدهای تله شما خطاها را به درستی مدیریت میکنند و در صورت لزوم پیامهای خطای آموزنده ارائه میدهند.
- از API رفلکت (Reflect) استفاده کنید: API
Reflectمتدهایی را ارائه میدهد که رفتار پیشفرض عملیات شیء را منعکس میکنند. از متدهایReflectدر متدهای تله خود استفاده کنید تا در صورت لزوم به رفتار اصلی واگذار کنید. این تضمین میکند که تلههای شما عملکرد موجود را مختل نمیکنند. - پروکسیهای خود را مستند کنید: هدف و رفتار پروکسیهای خود را به وضوح مستند کنید، از جمله تلههایی که استفاده میشوند و محدودیتهایی که اعمال میشوند. این به توسعهدهندگان دیگر کمک میکند تا کد شما را درک کرده و نگهداری کنند.
نتیجهگیری
پروکسیهای جاوااسکریپت ابزاری قدرتمند برای دستکاری و رهگیری پیشرفته اشیاء هستند. با درک و بهکارگیری الگوهای مختلف پروکسی، میتوانید کیفیت کد، امنیت و قابلیت نگهداری را افزایش دهید. از اعتبارسنجی ورودی کاربر گرفته تا کنترل دسترسی به منابع حساس، پروکسیها مکانیزمی انعطافپذیر و توسعهپذیر برای سفارشیسازی رفتار شیء ارائه میدهند. همانطور که امکانات پروکسیها را کاوش میکنید، به یاد داشته باشید که آنها را با دقت استفاده کرده و کد خود را به طور کامل مستند کنید.
مثالهای ارائه شده نشان میدهند که چگونه از پروکسیهای جاوااسکریپت برای حل مشکلات دنیای واقعی در یک زمینه جهانی استفاده کنید. با درک و بهکارگیری این الگوها، میتوانید برنامههای قویتر، ایمنتر و با قابلیت نگهداری بالاتر ایجاد کنید که نیازهای یک پایگاه کاربری متنوع را برآورده سازد. به یاد داشته باشید که همیشه پیامدهای جهانی کد خود را در نظر بگیرید و راهحلهای خود را با الزامات خاص مناطق و فرهنگهای مختلف تطبیق دهید.