Проблема слияния объекта с геттерами / сеттерами

1

Я сталкиваюсь с некоторыми проблемами с объединением двух объектов с помощью свойств getters/seters, которые деградируют в значения. Как это предотвратить? Нужно ли клонировать его вручную?

Ниже, надуманный пример моего pb:

function Factory1() {
  const p = {};

  return {
    get prop1 () { return p.prop; },
    set prop1 (value) { p.prop = value; }
  };
}

function Factory2() {
  const p = {};

  return {
    get prop2 () { return p.prop; },
    set prop2 (value) { p.prop = value; }
  }
}


const obj1 = Factory1();
const obj2 = Factory2();

const obj3a = {...Factory1(), ...Factory2()};
const obj3b = Object.assign(Factory1(), Factory2());

console.log('obj1.prop1 =>', Object.getOwnPropertyDescriptor(obj1, 'prop1'));
console.log('obj2.prop2 =>', Object.getOwnPropertyDescriptor(obj2, 'prop2'));

console.log('spread prop1 =>', Object.getOwnPropertyDescriptor(obj3a, 'prop1'));
console.log('spread prop2 =>', Object.getOwnPropertyDescriptor(obj3a, 'prop2'));
console.log('assign prop1 =>', Object.getOwnPropertyDescriptor(obj3b, 'prop1'));
console.log('assign prop2 =>', Object.getOwnPropertyDescriptor(obj3b, 'prop2'));

Обновление 2018-10-11

Простой пример:

function Factory1() {
  return {
    get prop1 () { return 42; }
  };
}

const obj1 = Factory1();
const obj2 = Object.assign({}, Factory1())

console.log('obj1.prop1 =>', Object.getOwnPropertyDescriptor(obj1, 'prop1'));
// result
// obj1.prop1 => { get: [Function],
//   set: undefined,
//   enumerable: true,
//   configurable: true }

console.log('obj2.prop1 =>', Object.getOwnPropertyDescriptor(obj2, 'prop1'));
// result
// obj2.prop1 => { value: 42,
//   writable: true,
//   enumerable: true,
//   configurable: true }


Более реальный пример

По запросу я попытаюсь проиллюстрировать, с каким типом кода я сталкиваюсь. Конечно, это конкатенация извлечений кода, отправленного в несколько модулей.

import axios from 'axios';

function createAxios (options = {}) {
  const axiosClient = axios.create(options);
  axiosClient.defaults.withCredentials = false;
  axiosClient.defaults.headers.common['Accept'] = 'text/html';

  return {axiosClient};
}

function WithQuery ({axiosClient}) {
  // This is a sample server that supports CORS.
  var sampleUrl = 'http://html5rocks-cors.s3-website-us-east-1.amazonaws.com/index.html';  

  return {
    query (url) {
      return axiosClient.get(url || sampleUrl);
    }
  };
}

function WithLastModified (context) {
  let lastModified;

  context.axiosClient.interceptors.response.use(function (response) {
    const {headers} = response;
    console.log('header last-modified:', headers['last-modified']);
    lastModified = headers['last-modified'];
    return response;
  });

  return {
    getLastModified () {
      return lastModified;
    },
    get lastModified () {
      return lastModified;
    }
  }
}

function build (options) {
  const httpClient = createAxios(options);
  return Object.assign({},
    httpClient,
    {search: WithQuery(httpClient)},
    WithLastModified(httpClient));
}

const api = build();
api.search.query().then(() => {
  console.log('get lastModified:', api.lastModified); // not working, always undefined
  console.log('getLastModified:', api.getLastModified()); // work
});

Этот тип заводских методов используется для ввода зависимостей и добавления функциональности через псевдомиксины к конечному объекту.

  • 0
    Какой именно ваш вариант использования? Если вы не хотите делиться закрытым объектом p , тогда да, вам придется создать метод ручного клонирования.
  • 0
    Возможные обходные пути здесь
Теги:

1 ответ

0
Лучший ответ

Если вы проверите свой код, вы заметите, что даже получая дескрипторы и добавляя их к новому объекту, ваши аксессоры будут иметь доступ к factoryN p внутренней области factoryN (эти factoryN доступа).

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

Вывод: если вы не перефразируете свой вопрос, чтобы объяснить, какова ваша фактическая цель, я бы сказал, что нет способа решить проблему.

Ответ на комментарий к OP

Я не уверен, что понимаю ваш оператор на отдельной локальной переменной p, определенной на 2 разных закрытиях. Моя забота о obj1.prop1 и obj2.prop2 get/set превращается в простое значение на obj3a

Возьмите свой собственный код:

function Factory1() {
  // This is a function scoped reference
  const p = {};

  return {
    get prop1 () { return p.prop; }, 
    set prop1 (value) { p.prop = value; }
  };
}

p - ссылка с привязкой к функциям, а все возвращенные аксессоры - это замыкания, которые фиксируют p.

Если вы возьмете эти аксессоры и добавите их в другой объект, p все равно укажет на захваченную ссылку. Таким образом, когда вы устанавливаете prop1 во многих объектах, все они будут устанавливать один и тот же целевой объект p !

  • 0
    Я не уверен, что понимаю ваше утверждение относительно отдельной локальной переменной p определенной для двух различных замыканий. Меня беспокоит то, что obj1.prop1 и obj2.prop2 get / set превратились в простые значения в obj3a .
  • 0
    @ Sté Посмотри мое обновление, на котором я тебе это объясню.
Показать ещё 12 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню