مفهوم proxy در جاوا اسکریپت

مفهوم proxy در جاوا اسکریپت

در ES6 ویژگی ای به نام Proxy در جاوااسکریپت معرفی شد. در این مقاله قصد داریم با هم به بررسی این ویژگی بپردازیم.

Proxy چیست؟

یک پروکسی جاوااسکریپت، یک شیء است که شیء دیگری را در بر می‌گیرد و امکان تعریف مجدد عملیات مختلف مثل خواندن، درج، اعتبارسنجی و غیره را در شیء به ما میدهد. به بیان دیگر پروکسی به شما این امکان را میدهد تا رفتار سفارشی را به یک شیء یا تابع اضافه کنید.

ساخت یک Proxy

برای ایجاد یک پروکسی، میتوان به صورت زیر عمل کرد:

let proxy = new Proxy(target, handler);

که در اینجا:

  • ()new Proxy – سازنده
  • target – شیء یا تابع هدف
  • handler – یک شیء شامل متد هایی برای کنترل رفتارِ target است. متد های درونِ شیء handler ، trap نامیده میشوند.

یک مثال ساده

ابتدا یک شیء با نام user تعریف میکنیم

const user = {
  firstName: 'John',
  lastName: 'Doe',
  age: 25,
  email: 'john.doe@example.com',
}

سپس، یک شیء handler تعریف میکنیم:

const handler = {
    get: function(obj, prop) {
        return obj[prop] ? obj[prop] : 'property does not exist';
    }
}

در قدم بعدی یک شیء پروکسی ایجاد میکنیم

const proxyUser = new Proxy(user, handler);
اکنون میتوانیم از پروکسی استفاده کنیم
console.log(proxyUser.firstName); // John
console.log(proxyUser.age); // 25
console.log(proxyUser.class); // property does not exist

شیء proxyUser از شیء user برای ذخیره اطلاعات استفاده میکند. همچنین proxyUser امکان دسترسی به تمام  property های user را دارد.

زمانی که به وسیله proxyUser به یک ویژگی از user دسترسی پیدا میکنیم، متد get() درون شیء handler فراخوانده میشود. همچنین اگر شیء اصلی (user) را تغییر دهیم، این تغییر در proxyUser نیز ایجاد خواهد شد.

user.firstName = 'Jane';
console.log(proxyUser.firstName); // Jane

و همچنین برعکس این قضیه هم برقرار است. یعنی تغییر در proxyUser، باعث تغییر در شیء اصلی خواهد شد:

proxyUser.lastName = 'William';
console.log(user.lastName); // William

با این حساب میتوانیم ادعا کنیم که userProxy (یا هر پروکسی که میسازیم) به عنوان یک "نماینده" برای شیء یا تابع اصلی عمل خواهد کرد. همانطور که دیدیم میتوانیم از یک پروکسی برای ایجاد عملکردهای جدید برای شیء استفاده کنیم. مثلا ممکن است بخواهید بررسی کنید آیا یک شیء دارای کلید خاصی است یا خیر و در صورت وجود داشتن کلید، اقدامی را انجام دهید. در چنین مواردی میتوان از پروکسی استفاده کرد.

شما همچنان میتوانید یک handler خالی را به پروکسی ارسال کنید. با این کار، پروکسی مانند شیء اصلی رفتار خواهد کرد. برای مثال:

let student = {
  name: 'Jack',
  age: 24
}

const handler = { };

const myProxy = new Proxy(student, {});

console.log(myProxy); // Proxy {name: "Jack", age: 24}
console.log(myProxy.name); // Jack

get() trap

متد ()get برای دسترسی به ویژگی های شیء اصلی استفاده میشود؛ به بیان دیگر، زمانیکه از طریق شیء پروکسی، به یک ویژگی از شیء اصلی دسترسی پیدا کنیم، این متد فراخوانده میشود. به صورت کلی، شما میتوانید یک عملکرد دلخواه برای این متد بنویسید تا هنگامی که property مورد دسترسی قرار گرفت اجرا شود. 

در مثال قبل، شیء user هیچ property با نام fullName نداشت. شما میتوانید از متد ()get استفاده کنید تا بر اساس firstName و lastName ، ویژگی مورد نظر را ایجاد کنید.

const user = {
  firstName: 'John',
  lastName: 'Doe'
}

const handler = {
  get(target, property) {
      return property === 'fullName' ?
          `${target.firstName} ${target.lastName}` :
          target[property];
  }
};

const proxyUser = new Proxy(user, handler);

console.log(proxyUser.fullName); // John Doe

set() trap

این متد، همانطور که از اسم آن میتوان حدس زد، برای set کردن value در شیء اصلی استفاده میشود. به عبارتی این متد، رفتار (عملکرد) را در زمانی که یک property در شیء اصلی set میشود، کنترل میکند. فرض کنید در شرایطی سن کاربر باید بزرگتر از 18 سال باشد. برای اعمال این شرط یک متد  set به صورت زیر ایجاد می‌کنیم:

const user = {
  firstName: 'John',
  lastName: 'Doe',
  age: 20
}

const handler = {
  set(target, property, value) {
      if (property === 'age') {
          if (typeof value !== 'number') {
              throw new Error('Age must be a number.');
          }
          else if (value < 18) {
              throw new Error('The user must be 18 or older.')
          }
      }
      target[property] = value;
  }
};

const proxyUser = new Proxy(user, handler);

ابتدا سن را به صورت رشته ست می‌کنیم:

proxyUser.age = 'eighteen'; // Error: Age must be a number.

این بار سن کاربر را برابر با 16 تعریف می‌کنیم:

proxyUser.age = '16'; // The user must be 18 or older.

در نهایت سن کاربر را برابر با 24 تعریف می‌کنیم:

proxyUser.age = 21;

همانطور که میبینیم هیچ اروری وجود ندارد.

موارد استفاده پروکسی ها

1. اعتبارسنجی

شما می‌توانید از یک پروکسی برای اعتبارسنجی استفاده کنید؛ به عنوان مثال می‌توانید مقدار یک property را چک کنید و بسته به مقدار آن، کاری را انجام دهید.

let student = {
    name: 'Jack',
    age: 24
}

const handler = {
    // get the object key and value
    get(obj, prop) {
    // check condition
    if (prop == 'name') {
      return obj[prop];
    } else {
      return 'Not allowed';
    }
  }
}

const proxy = new Proxy(student, handler);
console.log(proxy.name); // Jack
console.log(proxy.age); // Not allowed

در این مثال، فقط ویژگی name از شیء student قابل دسترسی میباشد و در غیر این صورت، “not allowed” برگردانده می‌شود.

2. Read-only object

در مواقعی شما قصد ندارید به دیگران اجازه دهید تا در یک شیء تغییری به وجود بیاورند. در این مواقع، می‌توانید از یک پروکسی استفاده کنید تا یک شیء را تنها قابل خواندن یا read-only کنید. به مثال زیر توجه کنید:

let student = {
  name: 'Jack',
  age: 23
}

const handler = {
  set: function (obj, prop, value) {
      if (obj[prop]) {
          // cannot change the object value
          console.log('Read only')
      }
  }
};

const proxy = new Proxy(student, handler);

proxy.name = 'John'; // Read only
proxy.age = 33; // Read only

3. کنترل اثرات جانبی

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

const myFunction = () => {
  console.log("execute this function")
};

const handler = {
  set: function (target, prop, value) {
      if (prop === 'name' && value === 'Jack') {
          // calling another function
          myFunction();
      }
      else {
          console.log('Can only access name property');
      }
  }
};

const proxy = new Proxy({}, handler);

proxy.name = 'Jack'; // execute this function
proxy.age = 33; // Can only access name property

مفهوم پروکسی در نسخه ES6 جاوااسکریپت معرفی شد. توجه داشته باشید که ممکن است بعضی از مرورگر ها به طور کامل از این ویژگی پشتیبانی نکنند. برای اطلاع از این موضوع، JavaScript proxy را مطالعه کنید.

از بهترین نوشته‌های کاربران سکان آکادمی در سکان پلاس