如何序列化对象并将其转换回与原始对象相同的 class

How to serialize an object and cast it back to the same class as the original object

我是 JavaScript 的新手,如果我问的不是 "how you do it in JavaScript",请多多包涵。欢迎就其他方法提出建议。

我有一个名为 State 的 class,我需要使用 JSON.stringify() 序列化那个 class 的对象。下一步是将它们反序列化回一个对象。但是,我的 class 使用 setter 和 getter。

我面临的问题是,在我反序列化这些对象后,setter 和 getter 似乎消失了。我只是不知道如何正确地将序列化对象转回 class 的对象,以便它们的行为与直接使用 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;

我希望每次设置 newStateX 对象的 demoParam 属性 时,我都会看到一条控制台日志消息。然而。我只看到它两次,即每个 new State(window.location.href) 语句。

我已经使用了问题的答案。但是,它没有按预期工作。

当您序列化一个对象时,您会触发 class' 实例的 toStringtoJSON 方法,最终只会得到一个 "dumb" JSON 表示您的可枚举属性。

如果您想重新创建一个行为与序列化之前一样的实例,您需要在 toJSON 函数中设置一个额外的 key/value 对,例如 ___internalType: 'state' 和然后稍后使用例如。一个 switch 语句,用于使用 new MyClass(serializedData) 重新创建您的特定 class 并传入您的序列化实例。在 class 的构造函数中,您设置了所有需要的属性,瞧,您又拥有了 "old" 实例

/编辑:澄清你的控制台日志没有显示的原因是因为你没有重新创建你的 class 的实例,而只是创建一个新的普通对象。

您可以使用 Object.assign 将普通对象数据复制到 class 的 "empty" 新实例中,代码如下:

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

在 JavaScript 中,我个人喜欢避免对我的数据对象使用 classes。 TypeScript 提供了一些更好的机会来解决这个问题,其中之一是 TypedJSON:

https://github.com/JohnWeisz/TypedJSON