JSON строит свойство класса ES6 с помощью getter/setter
У меня есть класс 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
, чтобы избежать запуска JS с использованием get/set, что приводит к бесконечному циклу, если я устанавливаю непосредственно на property
.
Теперь мне нужно подстроить экземпляр MyClass, чтобы отправить его с помощью HTTP-запроса. Стробируемый JSON - это такой объект, как:
{
//...
_property:
}
Мне нужна результирующая строка JSON для сохранения property
, поэтому служба, которую я отправляю, может правильно ее проанализировать. Мне также нужно property
оставаться в конструкторе, потому что мне нужно построить экземпляры MyClass из JSON, отправленные службой (которая отправляет объекты с property
not _property
).
Как мне обойти это? Должен ли я просто перехватить экземпляр MyClass перед отправкой его в HTTP-запрос и мутировать _property
to property
с помощью regex? Это кажется уродливым, но я смогу сохранить текущий код.
В качестве альтернативы я могу перехватить JSON, отправляемый клиенту из службы, и создать экземпляр MyClass с совершенно другим именем свойства. Однако это означает другое представление класса с обеих сторон службы.
Ответы
Ответ 1
Вы можете использовать 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
}
}
}
Ответ 2
Если вы хотите избежать вызова 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;
}
}
Ответ 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"}
Ответ 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
Используйте личные поля для внутреннего использования.
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")));