JavaScript Proxy: Управление объектами, функциями и классами (Часть 1)

Прокси в JavaScript — мощный инструмент, позволяющий создавать «ловушки» для объектов, функций и классов. Тема обширна, поэтому разберем её в двух частях. Эта часть посвящена теории, практическое применение будет рассмотрено во второй части.

Объекты и Proxy

Создадим обычный объект person:

const person = {
  name: 'Владилен',
  age: 25,
  job: 'Full stack'
};

Теперь запроектируем этот объект с помощью Proxy:

const proxy = new Proxy(person, {
  get: (target, prop) => {
    console.log('getting', prop); // Ловушка для метода get
    return target[prop];
  }
});

Обращаясь к proxy.name или proxy.age, мы получим значения из объекта person, но при этом сработает наша «ловушка» get, выводящая информацию в консоль. Идея Proxy заключается в перехвате операций над объектом и возможности модификации их поведения.

Proxy предоставляет несколько методов для перехвата различных операций:

  • get: Перехватывает чтение свойства объекта.
  • set: Перехватывает попытки изменения значений свойств объекта. Можно добавить валидацию или другие действия перед изменением. Пример:
set: (target, prop, value) => {
  if (prop in target) {
    target[prop] = value;
  } else {
    throw new Error(`New prop field ${prop} in target`);
  }
  return true;
},
  • has (in): Проверяет существование свойства в объекте. Позволяет реализовать кастомную логику проверки. Пример:
has: (target, prop) => {
  const allowedProps = ['name', 'age', 'job'];
  return allowedProps.includes(prop);
},
  • deleteProperty: Перехватывает удаление свойства. Можно добавить логирование или другие действия. Пример:
deleteProperty: (target, prop) => {
  console.log('deleting', prop);
  delete target[prop];
  return true;
},

Полная документация по методам Proxy доступна по ссылке (ссылка будет добавлена в соответствующее место).

Функции и Proxy

Рассмотрим пример проксирования функции:

function log(text) {
  return `log: ${text}`;
}

const fnProxy = new Proxy(log, {
  apply: (target, thisArg, args) => {
    console.log('calling fn');
    const result = target.apply(thisArg, args);
    return result.toUpperCase(); // Преобразуем результат в верхний регистр
  }
});

Метод apply перехватывает вызов функции. Мы можем выполнить дополнительную логику до или после вызова исходной функции и изменить результат.

Классы и Proxy

Проксирование классов позволяет перехватывать создание новых экземпляров:

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

const PersonProxy = new Proxy(Person, {
  construct: (target, args) => {
    console.log('construct');
    return new target(...args);
  }
});

Метод construct перехватывает вызов оператора new. Мы можем добавить логирование или выполнить другие действия перед созданием экземпляра класса. Можно использовать и другие методы Proxy, например, get для перехвата доступа к свойствам экземпляра.

Динамическое построение ключей

Рассмотрим пример нестандартного использования get:

// ... (код Proxy с get) ...

get: (target, prop) => {
  if (!(prop in target)) {
    const parts = prop.split('_');
    let result = '';
    for (const part of parts) {
      result += target[part] + ' ';
    }
    return result.trim();
  }
  return target[prop];
},

Этот код позволяет динамически создавать значения свойств, если запрашиваемое свойство отсутствует. Например, proxy.name_age_job вернет конкатенацию значений свойств name, age и job.

Proxy — мощный инструмент для расширения функциональности объектов, функций и классов в JavaScript. Он позволяет перехватывать различные операции и добавлять кастомную логику. Во второй части мы рассмотрим практическое применение Proxy.

Что будем искать? Например,программа