Search
Write a publication
Pull to refresh

Comments 10

В чем разница?

  • В первом варианте код более громоздкий и менее гибкий.

  • Во втором варианте Reflect выполняет стандартные операции, оставляя в ловушках только дополнительную логику.

Я один не увидел большой разницы? То ли пример неудачный, то ли... нет никакой разницы...

Тоже, честно признаться, не до конца понял. Что такое прокси и для чего, вопросов обычно не вызывает. Но чем может обернуться мутирование свойства целевого объекта напрямую в примерах не увидел. И чем тут рефлект помогает да, тоже, честно говоря, непонятно. Кроме того, что это какая-то чужеродная конструкция, других ощущений нет :)

Когда свойство мутируется напрямую, прокси это не контролирует. Но если мы используем Reflect, он помогает сохранить стандартное поведение объекта, обеспечивая предсказуемость. Например, если в ловушке set забыть вернуть true, присвоение свойства не сработает, а Reflect.set делает это автоматически. То есть он помогает писать так, чтобы не ломать стандартные механики JS.

Вот поискал примеры, думаю вот тут видно о чем речь:

Без Reflect

const target = { value: 10 };

const proxy = new Proxy(target, {
  set(obj, prop, val) {
    if (prop === 'value' && val < 0) {
      console.log('Значение не может быть отрицательным!');
      return false; // Операция не выполнится, но код не бросит ошибку
    }
    obj[prop] = val; // Прямая мутация без стандартного поведения
    return true; // Без этого могут быть неожиданные ошибки
  }
});

proxy.value = -5; // Значение не изменится, но ошибка в консоли не будет очевидной
console.log(target.value); // 10 (но это неочевидно, так как код не дал нам сигнал)


С Reflect

const target = { value: 10 };

const proxy = new Proxy(target, {
  set(obj, prop, val) {
    if (prop === 'value' && val < 0) {
      console.log('Значение не может быть отрицательным!');
      return false;
    }
    return Reflect.set(obj, prop, val); // Гарантирует стандартное поведение
  }
});

proxy.value = -5; // Ловушка блокирует изменение
console.log(target.value); // 10

proxy.value = 20; // Изменение проходит
console.log(target.value); // 20

В первом случае, если забыть вернуть true, код может работать нестабильно.

Во втором Reflect.set гарантирует, что объект ведет себя так же, как обычный JS-объект, но с дополнительной логикой.

Если менять свойство напрямую (target.value = -5), прокси это не перехватит, а при использовании proxy.value = -5 - контроль остается.

Почему в 1 случае ошибка в консоли не очевидна, если написано одно и то же?) в обоих случаях ошибка будет в консоли. Что-то с вашим примером не то

Я имел в виду не отсутствие ошибки, а то, что первый вариант может вести себя "скрытно". Без Reflect.set, если забыть вернуть true, операция присвоения технически проваливается, но не выбрасывает ошибку, а просто "молча" не обновляет свойство) Это может привести к багам, которые сложно отследить. С Reflect такого не случится)

Попробую объяснить разницу в более явной ситуации, например, когда объект имеет прототип:
Без Reflect (потенциально проблемный вариант)

const proto = { country: "Россия" };
const target = Object.create(proto);
target.name = "Дмитрий";

const proxy = new Proxy(target, {
  get(target, prop) {
    if (prop in target) {
      console.log(`Получаем свойство "${prop}": ${target[prop]}`);
      return target[prop];
    } else {
      throw new Error(`Свойство "${prop}" не существует`);
    }
  }
});

console.log(proxy.name);    // "Получаем свойство 'name': Дмитрий"
console.log(proxy.country); // Ошибка: Свойство "country" не существует

Проблема: Мы явно проверяем prop in target, но это работает только для собственных свойств объекта. Свойство country находится в прототипе, но его прокси не видит.

С Reflect:

const proto = { country: "Россия" };
const target = Object.create(proto);
target.name = "Дмитрий";

const proxy = new Proxy(target, {
  get(target, prop, receiver) {
    if (!Reflect.has(target, prop)) {
      throw new Error(`Свойство "${prop}" не существует`);
    }
    console.log(`Получаем свойство "${prop}": ${Reflect.get(target, prop, receiver)}`);
    return Reflect.get(target, prop, receiver);
  }
});

console.log(proxy.name);    //  "Получаем свойство 'name': Дмитрий"
console.log(proxy.country); //  "Получаем свойство 'country': Россия"

Reflect.has(target, prop) теперь проверяет включая прототипы.

Reflect.get(target, prop, receiver) корректно получает значение из цепочки прототипов.

Если объект использует прототипы, первый вариант не работает корректно — он не видит унаследованные свойства. Reflect решает эту проблему, сохраняя стандартное поведение объекта. В простых примерах разница может быть неочевидна, согласен.

Воу, в кои-то веки пост в корпоративный блог, в комментариях которого автор отвечает на вопросы! Мне уже стало казаться, что так просто не принято. Уже за это плюс) а если предметно, то как раз всё думал, а как вообще прокси-то используют на деле. Сам применял только для подмены запросов через консоль разработчика, но это же совсем не про разработку. Слышал, что во Vue активно используются прокси, а как именно - не знаю, не работал с ним) в общем спасибо за разъяснения, очень полезный пост

Спасибо. Стейт менеджер MobX весь на прокси. Я постараюсь про это написать в следующей статье)

Sign up to leave a comment.