J'ai une classe JavaScript ES6 qui a une propriété définie avec set
et accessible avec les fonctions get
. C'est aussi un paramètre constructeur pour que la classe puisse être instanciée avec ladite propriété.
class MyClass {
constructor(property) {
this.property = property
}
set property(prop) {
// Some validation etc.
this._property = prop
}
get property() {
return this._property
}
}
J'utilise _property
pour échapper au piège JS de l'utilisation de get/set qui entraîne une boucle infinie si je mets directement à property
.
Maintenant, je dois stringifier une instance de MyClass pour l'envoyer avec une requête HTTP. Le JSON stringifié est un objet comme:
{
//...
_property:
}
J'ai besoin de la chaîne JSON résultante pour conserver property
afin que le service auquel je l'envoie puisse l'analyser correctement. J'ai également besoin de property
pour rester dans le constructeur car j'ai besoin de construire des instances de MyClass à partir de JSON envoyées par le service (qui envoie des objets avec property
pas _property
).
Comment puis-je contourner cela? Dois-je simplement intercepter l'instance MyClass avant de l'envoyer à la demande HTTP et muter _property
à property
en utilisant l'expression régulière? Cela semble moche, mais je pourrai conserver mon code actuel.
Alternativement, je peux intercepter le JSON envoyé au client à partir du service et instancier MyClass avec un nom de propriété totalement différent. Cependant, cela signifie une représentation différente de la classe de chaque côté du service.
Vous pouvez utiliser méthode toJSON
pour personnaliser la façon dont votre classe se sérialise en 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
}
}
}
Si vous voulez éviter d'appeler toJson, il existe une autre solution utilisant énumérable et inscriptible:
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;
}
}
Comme mentionné par @Amadan, vous pouvez écrire votre propre méthode toJSON
.
De plus, afin d'éviter de mettre à jour votre méthode chaque fois que vous ajoutez une propriété à votre classe, vous pouvez utiliser une implémentation toJSON
plus générique.
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"}
Si vous souhaitez émettre toutes les propriétés et tous les champs , vous pouvez remplacer const jsonObj = {};
avec
const jsonObj = Object.assign({}, this);
Alternativement, si vous souhaitez émettre toutes les propriétés et certains champs spécifiques , vous pouvez le remplacer par
const jsonObj = {
myField: myOtherField
};
J'ai fait quelques ajustements au script d'Alon Bar. Voici une version du script qui fonctionne parfaitement pour moi.
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;
}
Utilisez champs privés pour un usage interne.
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")));