Очевидное загрязнение несколькими прокси-объектами для одной цели

Я пытаюсь создать несколько прокси-оболочек для одного и того же целевого объекта в JavaScript, при этом каждая отдельная оболочка имеет немного разные свойства, которые влияют на функционирование упакованной функциональности. Эти свойства присваиваются и доступны из receiver объект в set а также get обработчики. Однако, когда я проверяю сгенерированные прокси, у всех из них есть набор свойств, который, как я ожидаю, будет назначен последнему созданному прокси.

const obj = {};

const proxies = ['one', 'two'].map(name => {
  console.log(`proxy ${name}`);

  const proxy = new Proxy(obj, {
    get: (target, prop, receiver) => {
      if (prop === 'name') { return receiver.name; }

      return target[prop];
    },
    set: (target, prop, val, receiver) => {
      if (prop === 'name') {
        console.log(`setting name ${val} on receiver`);
        Object.defineProperty(receiver, prop, {
            value: val,
            configurable: true,
            enumerable: true}
        );
      } else {
        console.log(`setting ${prop} ${val} on target`);
        target[prop] = val;
      }

      return true;
    }
  });

  proxy.name = name;

  return proxy;
});

console.log();
console.log(proxies);

Мой ожидаемый результат: [{name: 'one'}, {name: 'two'}],

Фактический результат: [{name: 'two'}, {name: 'two'}], Даже если они кажутся идентичными, они не являются строго равными.

Если я опущу const obj и создавать мои объекты с new Proxy({}, ...) Я получаю ожидаемый результат - прокси one и прокси two, предположительно, поскольку целевая ссылка не разделяется между ними. Итак: что на земле? Из моего понимания, используя receiver хранить name должен препятствовать его распространению на целевой объект, но в любом случае, похоже, он это делает.

2 ответа

Решение

Ваш фрагмент

Object.defineProperty(receiver, prop, {
    value: val,
    configurable: true,
    enumerable: true}
);

не собирается делать то, что (я думаю) вы ожидаете от этого. поскольку receiver здесь объект прокси, определение свойства также будет прокси targetЭто означает, что различие между ветвями в вашем if/else почти ничего. Если вы хотите сохранить уникальное имя для каждого прокси-объекта, проще всего в этом случае использовать область действия замыкания, например

const proxies = ['one', 'two'].map(name => {
  console.log(`proxy ${name}`);

  const proxy = new Proxy(obj, {
    get: (target, prop, receiver) => {
      if (prop === 'name') { return name; }

      return Reflect.get(target, prop, receiver);
    },
    set: (target, prop, val, receiver) => {
      if (prop === 'name') {
        name = val;
        return true;
      }

      return Reflect.set(target, prop, val, receiver);
    },
    ownKeys: (target) => {
      return Reflect.ownKeys(target).concat('name');
    },
    getOwnPropertyDescriptor: (target, prop) => {
      if (prop === "name") return { enumerable: true, writable: true, configurable: true, value: name };

      return Reflect.getOwnPropertyDescriptor(target, prop);
    },
  });

  return proxy;
});

Похоже, это происходит при настройке свойств непосредственно на прокси. Поведение не связано с созданием нескольких прокси; создание одного прокси и настройка его name также загрязняет цель.

Использование наследующего объекта с его прототипом, настроенным на прокси, как подробно описано в этом ответе на связанный вопрос, не загрязняет цель прокси.

Другие вопросы по тегам