JSON stringify ES6 class property with getter/setter

萝らか妹 提交于 2019-11-26 17:22:56

问题


I have a JavaScript ES6 class that has a property set with set and accessed with get functions. It is also a constructor parameter so the class can be instantiated with said property.

class MyClass {
  constructor(property) {
    this.property = property
  }

  set property(prop) {
  // Some validation etc.
  this._property = prop
  }

  get property() {
    return this._property
  }
}

I use _property to escape the JS gotcha of using get/set that results in an infinite loop if I set directly to property.

Now I need to stringify an instance of MyClass to send it with a HTTP request. The stringified JSON is an object like:

{
   //...
   _property:
}

I need the resulting JSON string to preserve property so the service I am sending it to can parse it correctly. I also need property to remain in the constructor because I need to construct instances of MyClass from JSON sent by the service (which is sending objects with property not _property).

How do I get around this? Should I just intercept the MyClass instance before sending it to the HTTP request and mutate _property to property using regex? This seems ugly, but I will be able to keep my current code.

Alternatively I can intercept the JSON being sent to the client from the service and instantiate MyClass with a totally different property name. However this means a different representation of the class either side of the service.


回答1:


You can use toJSON method to customise the way your class serialises to 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:


If you want to avoid calling toJson, there is another solution using enumerable and writable:

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:


As mentioned by @Amadan you can write your own toJSON method.

Further more, in order to avoid re-updating your method every time you add a property to your class you can use a more generic toJSON implementation.

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

If you want to emit all properties and all fields you can replace const jsonObj = {}; with

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

Alternatively, if you want to emit all properties and some specific fields you can replace it with

const jsonObj = {
    myField: myOtherField
};



回答4:


I made some adjustments to the script of Alon Bar. Below is a version of the script that works perfectly for me.

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:


Use private fields for internal use.

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")));


来源:https://stackoverflow.com/questions/42107492/json-stringify-es6-class-property-with-getter-setter

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!