【问题标题】:How to serialize an object and cast it back to the same class as the original object如何序列化对象并将其转换回与原始对象相同的类
【发布时间】:2020-05-25 22:19:22
【问题描述】:

我是 JavaScript 新手,如果我要问的不是“你如何在 JavaScript 中做到这一点”,请耐心等待。欢迎提供其他方法的建议。

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

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

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

【问题讨论】:

    标签: javascript stringify ecmascript-2017


    【解决方案1】:

    当您序列化一个对象时,您会触发您的类实例的toStringtoJSON 方法,并最终得到可枚举属性的“愚蠢”JSON 表示。

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

    /edit:澄清控制台日志未显示的原因是因为您没有重新创建类的实例,而只是创建了一个新的普通对象。

    【讨论】:

    • 嗯,我想我希望有一个我还没有发现的机制。从我目前所见,JavaScript 类似乎是一个有问题且未完成的概念。没有适当的信息隐藏,与其他语言相比,继承的工作方式完全不同,我的用例也没有涵盖。似乎是为了我想要实现的目标而完全放弃课程的更好选择。
    • 是的,继承是通过与 java 等不同的原型链工作的。但在其他语言中,您需要在将对象序列化为 JSON/protobuf/string 后以某种方式对其进行水合。 private fields 将来可以实现信息隐藏(àla 私有变量),但您现在可以通过使用闭包来实现它
    【解决方案2】:

    您可以使用 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

    【讨论】:

    • 我上面提到的let newState3 = Object(assign new State(... 的结果不一样吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-01
    • 2023-03-13
    相关资源
    最近更新 更多