JSON 使用 getter/setter 对 ES6 类属性进行字符串化
Posted
技术标签:
【中文标题】JSON 使用 getter/setter 对 ES6 类属性进行字符串化【英文标题】:JSON stringify ES6 class property with getter/setter 【发布时间】:2017-06-25 17:43:37 【问题描述】:我有一个 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。然而,这意味着服务的任一侧对类的表示不同。
【问题讨论】:
MyClass.prototype.toJson
可能会帮助你
@StephenBugsKamenar: toJSON
- 注意大写
相关:Convert ES6 Class with Symbols to JSON
【参考方案1】:
我制作了一个名为 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
【讨论】:
【参考方案2】:使用私有字段供内部使用。
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")));
【讨论】:
【参考方案3】:正如@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
;
【讨论】:
这似乎同时产生了_property
和property
你是对的。我没有对问题的细节给予足够的关注:(尽管它很容易修复。只需省略 Object.assign 调用并将其替换为空对象。这样您就可以获得没有字段的所有属性。
我已经编辑了答案以更准确地适应所需的结果(和其他常见用例)。
我必须进行 2 处更改才能使此代码正常工作:将 const key of Object.keys(proto)
(仅返回可枚举属性)替换为 const key of Object.getOwnPropertyNames(proto)
(返回所有属性)和 jsonObj[key] = desc.get();
(返回“未定义”而不是属性值)与jsonObj[key] = this[key];
我实际上在 TypeScript 中使用它,它可以工作,但你是对的,我已经更新了使用 Object.getOwnPropertyNames
的答案,谢谢!至于第二部分,从现在可运行的 sn-p 中可以看出,它似乎可以工作。【参考方案4】:
我对 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;
【讨论】:
这个很好用,可以很容易地放入基类中。太好了!【参考方案5】:如果要避免调用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;
【讨论】:
你不要打电话给toJSON
。它由JSON.stringify
自动调用。请参阅JSON.stringify
第 12 步和SerializeJSONProperty
第 3 步。
这是一个很好的观点。无论如何,当你的类中有更多属性时,toJSON 方法会变得更大更复杂。每次添加/删除属性时都需要更新它。我仍然认为最好避免它。
仅供参考,这也适用于构造函数样式“类”。【参考方案6】:
您可以使用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
【讨论】:
我建议调用 getter,而不是._property
谢谢,早该想到的!以上是关于JSON 使用 getter/setter 对 ES6 类属性进行字符串化的主要内容,如果未能解决你的问题,请参考以下文章
如何一次生成构造函数、getter、setter、equals&hashcode 和 toString?