JSON使用getter/setter将ES6类属性字符串化

Tho*_*hia 19 javascript stringify ecmascript-6 es6-class

我有一个JavaScript ES6类,它具有一个属性集,set并通过get函数进行访问.它也是一个构造函数参数,因此可以使用所述属性实例化该类.

class MyClass {
  constructor(property) {
    this.property = property
  }

  set property(prop) {
  // Some validation etc.
  this._property = prop
  }

  get property() {
    return this._property
  }
}
Run Code Online (Sandbox Code Playgroud)

_property用来逃避使用get/set的JS问题,如果我直接设置它会导致无限循环property.

现在我需要对MyClass的一个实例进行字符串化,以便通过HTTP请求发送它.字符串化的JSON是一个对象,如:

{
   //...
   _property:
}
Run Code Online (Sandbox Code Playgroud)

我需要保留生成的JSON字符串,property以便我发送给它的服务可以正确解析它.我还需要property保留在构造函数中,因为我需要从服务发送的JSON构造MyClass的实例(它发送property没有的对象_property).

我该如何解决这个问题?如果我只是把它发送到HTTP请求之前截获MyClass的实例,并发生变异_property,以property使用正则表达式?这看起来很难看,但我能够保留当前的代码.

或者,我可以拦截从服务发送到客户端的JSON,并使用完全不同的属性名称实例化MyClass.但是,这意味着服务两侧的类的不同表示.

Ama*_*dan 27

您可以使用toJSON方法来自定义类序列化为JSON的方式:

class MyClass {
  constructor(property) {
    this.property = property
  }

  set property(prop) {
  // Some validation etc.
  this._property = prop
  }

  get property() {
    return this._property
  }

  toJSON() {
    return {
      property: this.property
    }
  }
}
Run Code Online (Sandbox Code Playgroud)


Ric*_*sár 12

如果你想避免调用toJson,还有另一种使用可枚举和可写的解决方案:

class MyClass {

  constructor(property) {

    Object.defineProperties(this, {
        _property: {writable: true, enumerable: false},
        property: {
            get: function () { return this._property; },
            set: function (property) { this._property = property; },
            enumerable: true
        }
    });

    this.property = property;
  }

}
Run Code Online (Sandbox Code Playgroud)

  • 这是一个好点。无论如何,当您在类中拥有更多属性时,toJSON方法将变得越来越大,越来越复杂。每次添加/删除属性时,都需要对其进行更新。我仍然认为最好避免这种情况。 (2认同)

Alo*_*Bar 7

如@Amadan所述,您可以编写自己的toJSON方法。

此外,为了避免每次将属性添加到类时都重新更新方法,可以使用更通用的toJSON实现。

class MyClass {

  get prop1() {
    return 'hello';
  }
  
  get prop2() {
    return 'world';
  }

  toJSON() {

    // start with an empty object (see other alternatives below) 
    const jsonObj = {};

    // add all properties
    const proto = Object.getPrototypeOf(this);
    for (const key of Object.getOwnPropertyNames(proto)) {      
      const desc = Object.getOwnPropertyDescriptor(proto, key);
      const hasGetter = desc && typeof desc.get === 'function';
      if (hasGetter) {
        jsonObj[key] = desc.get();
      }
    }

    return jsonObj;
  }
}

const instance = new MyClass();
const json = JSON.stringify(instance);
console.log(json); // outputs: {"prop1":"hello","prop2":"world"}
Run Code Online (Sandbox Code Playgroud)

如果要发出所有属性和所有字段,则可以替换const jsonObj = {};

const jsonObj = Object.assign({}, this);
Run Code Online (Sandbox Code Playgroud)

另外,如果要发出所有属性和某些特定字段,可以将其替换为

const jsonObj = {
    myField: myOtherField
};
Run Code Online (Sandbox Code Playgroud)

  • 我必须进行2处更改才能使此代码起作用:用Object.getOwnPropertyNames(proto)的const键(返回所有属性)和jsonObj替换Object.keys(proto)的const key(仅返回可枚举的属性)。 [key] = desc.get();`(返回“ undefined”,而不是属性的值),并带有“ jsonObj [key] = this [key];”。 (3认同)

bit*_*its 6

我对Alon Bar的脚本进行了一些调整。以下是一个非常适合我的脚本版本。

toJSON() {
        const jsonObj = Object.assign({}, this);
        const proto = Object.getPrototypeOf(this);
        for (const key of Object.getOwnPropertyNames(proto)) {
            const desc = Object.getOwnPropertyDescriptor(proto, key);
            const hasGetter = desc && typeof desc.get === 'function';
            if (hasGetter) {
                jsonObj[key] = this[key];
            }
        }
        return jsonObj;
    }
Run Code Online (Sandbox Code Playgroud)

  • 这个效果很好,可以很容易地放入基类中。伟大的! (2认同)