Object.defineProperty on a prototype prevents JSON.stringify from serializing it

前端 未结 5 885
花落未央
花落未央 2020-12-05 11:05

I\'m using TypeScript to define some classes and when I create a property, it generates the equivalent to Class1 in the following plunkr:

http://plnkr.c

相关标签:
5条回答
  • 2020-12-05 11:28

    As you've discovered, it won't serialize properties defined on prototype.

    I know it's not ideal, but another option is to do this:

    class Class1 {
        private _name = "test1";
    
        Name: string; // do this to make the compiler happy
    
        constructor() {
            Object.defineProperty(this, "Name", {
                get: function() { return this._name; },
                set: function(value) { this._name = value; },
                enumerable: true
            });
        }
    }
    

    Defining the property on the instance will serialize the property.

    0 讨论(0)
  • 2020-12-05 11:31

    Put something here hopefully can help others. What I have do for fix JSON.stringify not serialize properties defined on prototype

    var newObject = $.extend(false, {}, orginalObj);
    

    then I notice newObject have instance properties instead of prototype properties. I'm use typescript and get accessor.

    0 讨论(0)
  • 2020-12-05 11:34

    You can define a toJSON() method on your prototype to customize how instances are serialized.

    Class1.prototype.toJSON = function () {
        return {
            Name: this.Name
        };
    };
    JSON.stringify(new Class1()); // Will be '{"Name":"test1"}'
    
    0 讨论(0)
  • 2020-12-05 11:37

    If you like to push it forward, give a try to decorators proposal for TypeScript:

    According to this proposal (https://github.com/wycats/javascript-decorators):

    A decorator is:

    • an expression
    • that evaluates to a function
    • that takes the target, name, and property descriptor as arguments
    • and optionally returns a property descriptor to install on the target object

    It goes like this (high level view):

    Client code:

    @serializable()
    class Person {
    
        constructor(name: string) {
          this._name = name;
        }
    
        private _name: string;
    
        @serialize()
        get name() {
          return this._name;
        }
    
        @serialize('Language')
        get lang() {
          return 'JavaScript';
        }
    }
    

    Infrastructure:

    const serialized = new WeakMap();
    
    export function serializable(name?: string) {
        return function (target, propertyKey, descriptor) {
            target.prototype.toJSON = function () {
                const map = serialized.get(target.prototype);
                const props = Object.keys(map);
                return props.reduce((previous, key) => {
                    previous[map[key]] = this[key];
                    return previous;
                }, {});
            }
    
        }
    }
    
    export function serialize(name?: string) {
        return function (target, propertyKey, descriptor) {
            let map = serialized.get(target);
            if (!map) {
                map = {};
                serialized.set(target, map);
            }
    
            map[propertyKey] = name || propertyKey;
        }
    }
    

    UPDATE: I extracted this sample decorator into a repository at https://github.com/awerlang/es-decorators

    0 讨论(0)
  • 2020-12-05 11:45

    Yes, it is by design.

    As defined in ECMA, only own enumerable properties are serialized (stringify() is defined in terms of Object.keys(), among others).

    Property accessors are defined on prototypes, both in TypeScript and ES6.

    And answering your last question, that is the most eficient way to perform this operation.

    Beside that, only a) defining an toJSON() to each object part of the serialization, or b) passing a replacer function/array as 2nd argument to JSON.stringify().

    Whitelist properties from prototype:

    JSON.stringify(instance, Object.keys(instance.constructor.prototype))
    
    0 讨论(0)
提交回复
热议问题