مقدمه

با قابلیت Proxy می‌توانید انواع الگوهای برنامه‌نویسی پیشرفته را به سادگی پیاده‌سازی کنید؛ مثل اعتبارسنجی داده، محافظت از داده، ثبت رویداد (log)، ایجاد ویژگی‌های داینامیک یا حتی شبیه‌سازی مفاهیم ORM و reactivity. استفاده از این الگوها باعث می‌شود کد ما تمیزتر، ماژولارتر و قابل تست‌تر باشد و کنترل کاملی روی اشیا و رفتارها به ما می‌دهد.

الگوی لاگ‌گیری (Logging)

می‌توانید با Proxy تمامی عملیات روی شیء را لاگ کنید تا در توسعه و اشکال‌زدایی، از رفتار برنامه مطلع شوید. در مثال زیر هر بار مقدار یک پراپرتی خوانده یا نوشته شود، پیام ثبت می‌شود.

Copy Icon JAVASCRIPT
let data = { count: 1 };
let logger = new Proxy(data, {
  get(target, prop) {
    console.log(`Read ${prop}`);
    return target[prop];
  },
  set(target, prop, value) {
    console.log(`Write ${prop}=${value}`);
    target[prop] = value;
    return true;
  }
});

logger.count;      // Read count
logger.count = 5;  // Write count=5

در اینجا می‌توانید با افزودن شرط‌های بیشتر، فقط عملیات خاصی را لاگ کنید یا اطلاعات بیشتری (مانند زمان یا کاربر) ثبت کنید. همچنین می‌توانید لاگ‌ها را به سرور ارسال کنید یا در فایل ذخیره کنید تا برای تحلیل رفتار برنامه استفاده شوند.

الگوی اعتبارسنجی (Validation)

با Proxy می‌توانید فقط مقادیر مجاز را بپذیرید و بقیه را رد یا خطا ایجاد کنید. این کار بسیار برای فرم‌ها و داده‌های حساس کاربردی است. در مثال زیر، اعتبارسنجی به گونه‌ای انجام شده که فقط مقادیر مثبت پذیرفته شوند.

Copy Icon JAVASCRIPT
let pos = {};
let valid = new Proxy(pos, {
  set(target, prop, value) {
    if (value < 0) {
      throw new RangeError("invalid value");
    }
    target[prop] = value;
    return true;
  }
});
valid.x = 10;    // valid
valid.x = -5;    // error

در اینجا می‌توانید انواع شرط‌های اعتبارسنجی را اضافه کنید؛ مثلاً نوع داده، بازه مجاز، یا حتی اعتبارسنجی async (مثلاً بررسی وجود نام کاربری در سرور). همچنین می‌توانید پیام خطا را سفارشی کنید یا عملیات خاصی هنگام خطا انجام دهید. این الگو برای جلوگیری از ورود داده‌های نامعتبر به برنامه بسیار کاربردی است.

الگوی محافظت از داده (Protection)

گاهی اوقات می‌خواهیم برخی داده‌ها فقط توسط بخش خاصی از برنامه قابل مشاهده یا تغییر باشند. با استفاده از Proxy می‌توانیم دسترسی به پراپرتی‌هایی که با یک پیشوند خاص (مثلاً _ ) شروع می‌شوند را محدود کنیم تا از افشای اطلاعات حساس جلوگیری شود. این کار برای پیاده‌سازی encapsulation یا جلوگیری از دسترسی مستقیم به داده‌های داخلی بسیار مفید است. مثال زیر را ببینید.

Copy Icon JAVASCRIPT
let secret = { _hidden: "hidden", visible: "visible" };
let protector = new Proxy(secret, {
  get(target, prop) {
    if (prop.toString().startsWith("_"))
      return undefined;
    return target[prop];
  }
});
console.log(protector.visible);  // visible
console.log(protector._hidden); // undefined

در اینجا می‌توانید علاوه بر get، متد set را هم پیاده‌سازی کنید تا حتی نوشتن روی پراپرتی‌های حساس را نیز محدود کنید. مثلاً اگر کسی بخواهد مقدار پراپرتی‌هایی که با _ شروع می‌شوند را تغییر دهد، خطا دریافت کند یا عملیات نادیده گرفته شود. این روش برای پیاده‌سازی امنیت و جلوگیری از تغییرات ناخواسته روی داده‌های داخلی بسیار مفید است.

الگوهای پیشرفته (Dynamic/Reactive/Virtual)

پراکسی‌ها برای ساخت ساختارهای دینامیک (پراپرتی داینامیک)، ساخت سیستم‌های reactive (مثل Vue.js) یا حتی شبیه‌سازی دیتابیس‌های مجازی هم به کار می‌روند. هر جایی که بخواهید رفتار اشیا را در زمان اجرا تغییر دهید، پراکسی راه‌حل ایده‌آل است.

در مثال زیر اگر پراپرتی‌ای وجود نداشت، به شکل خودکار مقدار پیش‌فرض تولید می‌شود.

Copy Icon JAVASCRIPT
let def = {};
let dynamic = new Proxy(def, {
  get(target, prop) {
    if (!(prop in target)) {
      target[prop] = prop + "_value";
    }
    return target[prop];
  }
});

console.log(dynamic.foo); // foo_value
console.log(dynamic.bar); // bar_value

در اینجا می‌توانید پراکسی را طوری گسترش دهید که مثلاً مقدار پیش‌فرض بر اساس نوع پراپرتی، یا حتی با فراخوانی یک تابع async از سرور دریافت شود. همچنین می‌توانید برای پیاده‌سازی کش (cache)، lazy loading یا حتی ساختارهای ORM از این الگو استفاده کنید. این روش پایه بسیاری از فریم‌ورک‌های reactive مدرن است و به شما اجازه می‌دهد رفتار اشیا را به صورت کاملاً داینامیک و قابل کنترل تغییر دهید.