برنامه نویسی

اسرار پروکسی ها: رهگیری و کنترل اشیاء در JavaScript

در پشت صحنه اشیاء JavaScript ابزاری قدرتمند قرار دارد که به شما امکان می دهد هرگونه تعامل با اشیاء را رهگیری ، اصلاح و کنترل کنید. به دنیای پروکسی خوش آمدید.

مقدمه: پروکسی ها چیست؟

در JavaScript ، پراکسی ها به عنوان واسطه برای اشیاء عمل می کنند و به شما امکان می دهند رفتارهای اساسی مانند خواندن ملک ، تکلیف ارزش ، شمارش ، دعوت عملکرد و حتی عملیات را با آن سفارشی کنید new اپراتور معرفی شده در ES6 (ECMAScript 2015) ، با وجود ارائه امکانات چشمگیر ، پروکسی ها توسط بسیاری از توسعه دهندگان مورد استفاده قرار نمی گیرند.

یک پروکسی یک هدف هدف را می پیچد و تعریف می کند تله که در صورت انجام عملیات خاص بر روی شی انجام می شوند. این تله ها به شما امکان می دهد رفتار پیش فرض JavaScript را رهگیری و سفارشی کنید.

آناتومی یک پروکسی

ساختار اساسی یک پروکسی از دو مؤلفه اصلی تشکیل شده است:

const proxy = new Proxy(target, handler);
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

  • هدف: شیء اصلی که توسط پروکسی پیچیده می شود
  • کنترل کننده: یک شیء حاوی “تله” که رفتار سفارشی را تعریف می کند

تله های اساسی

JavaScript 13 تله مختلف را ارائه می دهد ، اما بیایید روی قدرتمندترین آنها تمرکز کنیم:

1. دریافت کنید

در get Trap Rejection Properts خوانده شده است. هر زمان که به یک ویژگی شی دسترسی داشته باشید ، شروع می شود.

const handler = {
  get(target, prop, receiver) {
    console.log(`Accessing property: ${prop}`);
    return Reflect.get(target, prop, receiver);
  }
};

const user = { name: "Anna", age: 28 };
const userProxy = new Proxy(user, handler);

console.log(userProxy.name); 
// Output:
// Accessing property: name
// Anna
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

2. مجموعه

در set هنگامی که مقادیر به خواص اختصاص می یابد ، تله ایجاد می شود.

const handler = {
  set(target, prop, value, receiver) {
    if (prop === 'age' && typeof value !== 'number') {
      throw new TypeError('Age must be a number');
    }
    console.log(`Setting ${prop} = ${value}`);
    return Reflect.set(target, prop, value, receiver);
  }
};

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

userProxy.name = "Charles"; // Setting name = Charles
userProxy.age = 30;         // Setting age = 30
try {
  userProxy.age = "thirty"; // Error!
} catch (e) {
  console.log(e.message);   // Age must be a number
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

3. درخواست کنید

تماس های عملکردی را به شما امکان می دهد تا نحوه اجرای یک عملکرد را تغییر دهید.

function add(a, b) {
  return a + b;
}

const handler = {
  apply(target, thisArg, args) {
    console.log(`Calling function with arguments: ${args}`);
    return Reflect.apply(target, thisArg, args);
  }
};

const addProxy = new Proxy(add, handler);
console.log(addProxy(5, 3));
// Output:
// Calling function with arguments: 5,3
// 8
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

4

رهگیری new اپراتور ، به شما این امکان را می دهد تا نحوه فوری اشیاء را کنترل کنید.

class Person {
  constructor(name) {
    this.name = name;
  }
}

const handler = {
  construct(target, args, newTarget) {
    console.log(`Creating new instance with: ${args}`);
    return Reflect.construct(target, args, newTarget);
  }
};

const PersonProxy = new Proxy(Person, handler);
const person = new PersonProxy("Daniel");
// Output: Creating new instance with: Daniel
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید


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

1. اعتبار سنجی خودکار

یکی از قدرتمندترین کاربردهای پروکسی ، اعتبار سنجی اتوماتیک بدون آلاینده کد شی اصلی است.

function createValidator(schema) {
  return {
    set(target, prop, value) {
      if (!schema[prop]) {
        target[prop] = value;
        return true;
      }

      // Type validation
      if (schema[prop].type && typeof value !== schema[prop].type) {
        throw new TypeError(`${prop} must be of type ${schema[prop].type}`);
      }

      // Format validation (regex)
      if (schema[prop].pattern && !schema[prop].pattern.test(value)) {
        throw new Error(`${prop} does not match the expected pattern`);
      }

      // Minimum value validation
      if (schema[prop].min !== undefined && value < schema[prop].min) {
        throw new RangeError(`${prop} must be >= ${schema[prop].min}`);
      }

      target[prop] = value;
      return true;
    }
  };
}

const userSchema = {
  name: { type: "string" },
  email: { 
    type: "string", 
    pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
  },
  age: { 
    type: "number", 
    min: 18 
  }
};

const user = new Proxy({}, createValidator(userSchema));

user.name = "Laura";   // OK
user.age = 25;         // OK
// user.email = "invalid";  // Error: email does not match the expected pattern
// user.age = 15;           // Error: age must be >= 18
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

2. اشیاء واکنشی (سبک vue.js)

vue.js از تکنیکی مشابه پروکسی ها برای ایجاد سیستم واکنش پذیری خود استفاده می کند. در اینجا یک اجرای ساده وجود دارد:

function reactive(obj) {
  const observers = new Map();

  return new Proxy(obj, {
    get(target, prop, receiver) {
      const value = Reflect.get(target, prop, receiver);

      // Collect the current dependency if it exists
      if (activeEffect && typeof value !== 'object') {
        if (!observers.has(prop)) {
          observers.set(prop, new Set());
        }
        observers.get(prop).add(activeEffect);
      }

      return value;
    },

    set(target, prop, value, receiver) {
      const result = Reflect.set(target, prop, value, receiver);

      // Notify observers
      if (observers.has(prop)) {
        observers.get(prop).forEach(effect => effect());
      }

      return result;
    }
  });
}

// Simplified tracking system
let activeEffect = null;

function watchEffect(fn) {
  activeEffect = fn;
  fn();  // Execute the function to track dependencies
  activeEffect = null;
}

// Usage
const state = reactive({ counter: 0 });

watchEffect(() => {
  console.log(`The counter is: ${state.counter}`);
});

// Initial output: The counter is: 0
state.counter++;    // Output: The counter is: 1
state.counter += 2; // Output: The counter is: 3
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

3. محافظت در برابر اصلاحات ناخواسته

پروکسی ها می توانند از اشیاء مهم در برابر اصلاحات ناخواسته محافظت کنند ، و همچنین دسترسی غیرمجاز را تشخیص دهند:

function protectObject(obj, allowedOperations = {}) {
  return new Proxy(obj, {
    get(target, prop) {
      if (allowedOperations.read && !allowedOperations.read.includes(prop)) {
        console.warn(`Unauthorized read of property: ${prop}`);
        return undefined;
      }
      return target[prop];
    },

    set(target, prop, value) {
      if (allowedOperations.write && !allowedOperations.write.includes(prop)) {
        console.error(`Unauthorized attempt to modify: ${prop}`);
        return false;
      }
      target[prop] = value;
      return true;
    },

    deleteProperty(target, prop) {
      console.error(`Attempt to delete property: ${prop}`);
      return false;
    }
  });
}

const secureConfig = protectObject(
  { 
    debug: true, 
    apiKey: 'sk_12345',
    timeout: 30000 
  },
  {
    read: ['debug', 'timeout'],
    write: ['timeout']
  }
);

console.log(secureConfig.debug);   // true
console.log(secureConfig.apiKey);  // Warning and undefined
secureConfig.timeout = 60000;      // Allowed
secureConfig.apiKey = 'new_key';   // Error: Unauthorized attempt
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

4. بارگذاری تنبل و حافظه پنهان املاک

ما می توانیم از پروکسی ها برای اجرای بارگیری تنبل و ذخیره سازی خواص محاسباتی گران استفاده کنیم:

function lazyProperties(initializer) {
  const values = {};
  const computed = {};

  return new Proxy({}, {
    get(target, prop) {
      if (!(prop in values)) {
        // If the property hasn't been calculated yet
        if (prop in initializer) {
          console.log(`Calculating value for ${prop}...`);

          // Memoization: save the result for future use
          values[prop] = initializer[prop]();
          console.log(`Value calculated and cached.`);
        }
      }
      return values[prop];
    }
  });
}

// Usage:
const data = lazyProperties({
  expensiveValue: () => {
    // Simulating an expensive operation
    console.log('Executing time-consuming calculation...');
    let result = 0;
    for (let i = 0; i < 1000000; i++) {
      result += Math.random();
    }
    return result / 1000000;
  },
  anotherExpensiveValue: () => {
    // Another expensive operation
    return fetch('https://api.example.com/data').then(r => r.json());
  }
});

console.log(data.expensiveValue);  // Calculates the first time
console.log(data.expensiveValue);  // Uses cached value
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید


خطرات و ملاحظات عملکرد

اگرچه قدرتمند است ، اما پروکسی ها محدودیت های مهمی دارند:

1. تأثیر عملکرد

پروکسی ها لایه ای از غیرمستقیم را اضافه می کنند که می تواند بر عملکرد تأثیر بگذارد ، به خصوص در عملیات با فرکانس بالا:

// Performance test
function testPerformance() {
  const obj = { value: 0 };
  const proxy = new Proxy(obj, {
    get: (target, prop) => Reflect.get(target, prop),
    set: (target, prop, value) => Reflect.set(target, prop, value)
  });

  console.time('Direct Object');
  for (let i = 0; i < 1000000; i++) {
    obj.value++;
  }
  console.timeEnd('Direct Object');

  console.time('Via Proxy');
  for (let i = 0; i < 1000000; i++) {
    proxy.value++;
  }
  console.timeEnd('Via Proxy');
}

testPerformance();
// Typical output:
// Direct Object: ~5ms
// Via Proxy: ~50ms
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

2. رفتار با روشهای داخلی

روش ها و خصوصیات داخلی یک شی می تواند به طور غیر منتظره ای در هنگام دسترسی از طریق یک پروکسی رفتار کند:

class MyClass {
  constructor() {
    this.value = 42;
  }

  method() {
    return this.value;
  }
}

const instance = new MyClass();
const proxy = new Proxy(instance, {});

console.log(instance.method()); // 42
console.log(proxy.method());    // Can cause issues if 'this' is used internally
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

3. پروکسی ها به عنوان مثال شفاف نیستند

class Example {}
const proxy = new Proxy(new Example(), {});

console.log(proxy instanceof Example); // false - this might surprise you!
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید


ادغام پروکسی با سایر ویژگی های مدرن

پروکسی با API بازتاب

API بازتاب در کنار پراکسی ها معرفی شد و روش هایی را ارائه می دهد که مطابق با تله های پروکسی است:

const object = { a: 1, b: 2 };
const proxy = new Proxy(object, {
  get(target, prop, receiver) {
    console.log(`Accessing ${prop}`);
    // Using Reflect.get instead of target[prop]
    return Reflect.get(target, prop, receiver);
  }
});

// Reflect also allows metaprogramming operations
console.log(Reflect.ownKeys(proxy)); // ['a', 'b']
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

پروکسی با نقشه ضعیف برای داده های خصوصی

ما می توانیم پروکسی ها را با مپ های ضعیف ترکیب کنیم تا خصوصیات واقعاً خصوصی ایجاد کنیم:

const privateData = new WeakMap();

class SecureUser {
  constructor(name, password) {
    const data = { name, password, passwordAttempts: 0 };
    privateData.set(this, data);

    return new Proxy(this, {
      get(target, prop) {
        if (prop === 'name') {
          return privateData.get(target).name;
        }
        if (prop === 'checkPassword') {
          return password => {
            const data = privateData.get(target);
            data.passwordAttempts++;

            if (data.passwordAttempts > 3) {
              throw new Error('Account locked after multiple attempts');
            }

            return data.password === password;
          };
        }
        return target[prop];
      }
    });
  }
}

const user = new SecureUser('john', 'password123');
console.log(user.name);                // 'john'
console.log(user.checkPassword('wrong')); // false
console.log(user.checkPassword('wrong')); // false
console.log(user.checkPassword('wrong')); // false
// user.checkPassword('wrong');           // Error: Account locked
console.log(user.password);              // undefined - not accessible
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید


الگوهای پیشرفته با پروکسی ها

مجاورت عمیق

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

function deepProxy(obj, handler) {
  if (typeof obj !== 'object' || obj === null) {
    return obj;
  }

  // Process arrays and objects recursively
  for (const key of Object.keys(obj)) {
    if (typeof obj[key] === 'object' && obj[key] !== null) {
      obj[key] = deepProxy(obj[key], handler);
    }
  }

  return new Proxy(obj, handler);
}

// Handler that logs all operations
const logHandler = {
  get(target, prop, receiver) {
    const value = Reflect.get(target, prop, receiver);
    console.log(`GET: ${prop}`);
    return value;
  },
  set(target, prop, value, receiver) {
    console.log(`SET: ${prop} = ${value}`);
    return Reflect.set(target, prop, value, receiver);
  }
};

const data = deepProxy({
  user: {
    profile: {
      name: 'Anna',
      contact: {
        email: 'anna@example.com'
      }
    },
    preferences: {
      theme: 'dark'
    }
  }
}, logHandler);

// Nested access tracked
data.user.profile.contact.email; // Logs all intermediate accesses
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

الگوی: پروکسی های قابل برگشت برای کنترل دسترسی موقت

JavaScript اجازه می دهد تا پروکسی های قابل بازگشت ، برای اعطای دسترسی موقت مفید باشد:

function createTemporaryAccess(obj, timeoutMs = 30000) {
  const { proxy, revoke } = Proxy.revocable(obj, {});

  setTimeout(() => {
    console.log('Access automatically revoked');
    revoke();
  }, timeoutMs);

  return proxy;
}

const sensitiveData = { apiKey: '12345', secret: 'confidential value' };
const temporaryAccess = createTemporaryAccess(sensitiveData, 5000);

// Immediate use works
console.log(temporaryAccess.apiKey); // '12345'

// After 5 seconds...
setTimeout(() => {
  try {
    console.log(temporaryAccess.apiKey);
  } catch (e) {
    console.log('Error:', e.message); // TypeError: Cannot perform 'get' on a proxy that has been revoked
  }
}, 6000);
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید


نتیجه گیری:

پروکسی ها یکی از قدرتمندترین موارد اضافی در JavaScript مدرن را نشان می دهند ، و این امکان را فراهم می کند تا پیشرفته سازی پیشرفته را انجام دهد و نحوه تعامل ما با اشیاء را تغییر دهد. چارچوب هایی مانند vue.js در حال حاضر از این فناوری برای ایجاد سیستم های واکنشی ظریف استفاده می کنند.

نکات نهایی برای استفاده مؤثر از پروکسی ها:

  1. از کمبود استفاده کنید: پروکسی ها پیچیدگی را اضافه می کنند و می توانند بر عملکرد تأثیر بگذارند.
  2. زمینه را در نظر بگیرید: در برنامه های بحرانی با کارایی بالا ، فقط در جایی که هزینه عملکرد قابل قبول است ، از پروکسی استفاده کنید.
  3. با API های دیگر ترکیب کنید: تأمل ، مپ ضعیف و کلاس ها با پروکسی ها بسیار خوب کار می کنند.
  4. کاملاً آزمون: رفتار پروکسی می تواند ظریف باشد ، به خصوص با وراثت و روشهای داخلی.

با تسلط بر پروکسی ها ، درهای الگوهای برنامه نویسی پیشرفته را باز می کنید که می تواند کد شما را قوی تر ، ایمن تر و بیان کند.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا