Как сериализовать объект и вернуть его к тому же классу, что и исходный объект

Я новичок в JavaScript, так что потерпите меня, если я спрашиваю не «как вы это делаете в JavaScript». Советы по другим подходам приветствуются.

У меня есть класс с именем State, и мне нужно сериализовать объекты этого класса, используя JSON.stringify(). Следующим шагом будет их десериализация обратно в объекты. Однако мой класс использует сеттеры и геттеры.

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

На другом языке я бы превратил эти объекты в State объектов. Я не могу найти механизм JavaScript, который работает таким образом.

Код выглядит следующим образом:

class State {
  constructor(href) {
    this.url = href;
  }

  set url(href) {
    this._url      = new URL(href);
    this.demoParam = this._url.searchParams.get("demoParam");
  }

  get url() {
    return this._url;
  }

  set demoParam(value) {
    let param = parseInt(value, 10);
    if(isNaN(param)) {
      param = 2;
    }
    console.log("Setting 'demoParam' to value " + param);
    this._demoParam = param;
  }

  get demoParam() {
    return this._demoParam;
  }

  toJSON() {
    let stateObject  = {};
    const prototypes = Object.getPrototypeOf(this);
    for(const key of Object.getOwnPropertyNames(prototypes)) {
      const descriptor = Object.getOwnPropertyDescriptor(prototypes, key);
      if(descriptor && typeof descriptor.get === 'function') {
        stateObject[key] = this[key];
      }
    }
    return stateObject;
  }
}

let originalState = new State(window.location.href);

let newState1 = JSON.parse(JSON.stringify(originalState));
newState1.demoParam = 12;

let newState2 = Object.create(State.prototype, Object.getOwnPropertyDescriptors(JSON.parse(JSON.stringify(originalState))));
newState2.demoParam = 13;

let newState3 = Object.assign(new State(window.location.href), JSON.parse(JSON.stringify(originalState)));
newState3.demoParam = 14;

let newState4 = Object.setPrototypeOf(JSON.parse(JSON.stringify(originalState)), State.prototype);
newState4.demoParam = 15;

Я ожидаю, что каждый раз, когда я устанавливаю свойство demoParam объекта newStateX, я буду видеть сообщение журнала консоли. Однако. Я вижу это только дважды, то есть для каждого оператора new State(window.location.href).

Я использовал ответ на этот вопрос. Однако это не работает должным образом.


person ackh    schedule 10.02.2020    source источник


Ответы (2)


когда вы сериализуете объект, вы запускаете метод toString или toJSON экземпляра вашего класса и в конечном итоге получаете просто "тупое" JSON-представление ваших перечисляемых атрибутов.

если вы хотите воссоздать экземпляр, который ведет себя так же, как и до сериализации, вам нужно будет установить дополнительную пару ключ/значение в вашей функции toJSON, например ___internalType: 'state', а затем использовать позже, например. оператор switch для воссоздания вашего конкретного класса с передачей new MyClass(serializedData)and в ваш сериализованный экземпляр. В конструкторе вашего класса вы устанавливаете все необходимые атрибуты и вуаля, у вас снова есть ваш «старый» экземпляр

/edit: чтобы уточнить причину, по которой ваши журналы консоли не отображаются, заключается в том, что вы не воссоздаете экземпляр своего класса, а просто создаете новый простой объект.

person japrescott    schedule 10.02.2020
comment
Хм, наверное, я надеялся на механизм, который еще не открыл. Из того, что я видел до сих пор, классы JavaScript кажутся сомнительной и незавершенной концепцией. Нет надлежащего сокрытия информации, наследование работает совершенно по-другому по сравнению с другими языками, и мой вариант использования также не рассматривается. Кажется, это лучший вариант вообще отказаться от занятий ради того, чего я пытаюсь достичь. - person ackh; 10.02.2020
comment
да, наследование работает через цепочку прототипов, которая отличается от java и т. д. Но и в других языках вам нужно каким-то образом гидратировать свой объект после того, как вы сериализовали его в JSON/protobuf/string. сокрытие информации (например, частные переменные) станет возможным в будущем с помощью частные поля, но вы можете сделать это сейчас, используя замыкания - person japrescott; 10.02.2020

Вы можете использовать Object.assign для копирования данных простого объекта в «пустой» новый экземпляр класса по строкам этого кода:

function cast(o) {
   if (!o._cls) return o;
   var _cls = eval(o._cls);
   return Object.assign(new _cls(), o);
}

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

https://github.com/JohnWeisz/TypedJSON

person ZPiDER    schedule 10.02.2020
comment
Разве это не тот же результат, о котором я уже рассказал let newState3 = Object(assign new State(... выше? - person ackh; 10.02.2020