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

时间:2017-02-08 07:52:25

标签: javascript ecmascript-6 stringify 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
  }
}

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

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

{
   //...
   _property:
}

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

我如何解决这个问题?我应该在将MyClass实例发送到HTTP请求之前拦截它并使用正则表达式将_property变为property吗?这看起来很难看,但我能保留现有的代码。

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

6 个答案:

答案 0 :(得分:22)

您可以使用toJSON method自定义类序列化为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
    }
  }
}

答案 1 :(得分:8)

如果你想避免调用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;
  }

}

答案 2 :(得分:4)

如@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"}

如果您要发布所有属性和所有字段,可以将const jsonObj = {};替换为

const jsonObj = Object.assign({}, this);

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

const jsonObj = {
    myField: myOtherField
};

答案 3 :(得分:3)

我对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;
    }

答案 4 :(得分:0)

使用私有字段供内部使用。

class PrivateClassFieldTest {
    #property;
    constructor(value) {
        this.property = value;
    }
    get property() {
        return this.#property;
    }
    set property(value) {
        this.#property = value;
    }
}

class Test {
	constructor(value) {
		this.property = value;
	}
	get property() {
		return this._property;
	}
	set property(value) {
		this._property = value;
	}
}

class PublicClassFieldTest {
	_property;
	constructor(value) {
		this.property = value;
	}
	get property() {
		return this.property;
	}
	set property(value) {
		this._property = value;
	}
}

class PrivateClassFieldTest {
	#property;
	constructor(value) {
		this.property = value;
	}
	get property() {
		return this.#property;
	}
	set property(value) {
		this.#property = value;
	}
}

console.log(JSON.stringify(new Test("test")));
console.log(JSON.stringify(new PublicClassFieldTest("test")));
console.log(JSON.stringify(new PrivateClassFieldTest("test")));

答案 5 :(得分:0)

我制作了一个名为 esserializer 的 npm 模块来解决这样的问题:将 JavaScript 类的实例字符串化,以便它可以与 HTTP 请求一起发送:

// Client side
const ESSerializer = require('esserializer');
const serializedText = ESSerializer.serialize(anInstanceOfMyClass);
// Send HTTP request, with serializedText as data

在服务端,再次使用 esserializer 将数据反序列化为 anInstanceOfMyClass 的完美副本,保留所有 getter/setter 字段(例如 property):

// Node.js service side
const deserializedObj = ESSerializer.deserialize(serializedText, [MyClass]);
// deserializedObj is a perfect copy of anInstanceOfMyClass