我有一个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。但是,这意味着服务任一侧的类的不同表示。
答案 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