原型上的Object.defineProperty阻止JSON.stringify序列化它

Chr*_*ers 18 javascript json typescript

我正在使用TypeScript来定义一些类,当我创建一个属性时,它会生成Class1以下plunkr中的等价物:

http://plnkr.co/edit/NXUo7zjJZaUuyv54TD9i?p=preview

var Class1 = function () {
  this._name = "test1";
}

Object.defineProperty(Class1.prototype, "Name", {
  get: function() { return this._name; },
  set: function(value) { this._name = value; },
  enumerable: true
});

JSON.stringify(new Class1()); // Will be "{"_name":"test1"}"
Run Code Online (Sandbox Code Playgroud)

序列化时,它不会输出我刚刚定义的属性.

instance2并且instance3通过序列化已定义的属性来表现我的期望.(参见plunkr输出).

我的实际问题是:这是正常的吗?

如果是这样,我该如何以最有效的方式解决它?

zzz*_*Bov 19

您可以toJSON()在原型上定义方法以自定义实例的序列化方式.

Class1.prototype.toJSON = function () {
    return {
        Name: this.Name
    };
};
JSON.stringify(new Class1()); // Will be '{"Name":"test1"}'
Run Code Online (Sandbox Code Playgroud)


And*_*ang 17

如果您想推进它,请尝试针对TypeScript的装饰器提议:

根据这个提议(https://github.com/wycats/javascript-decorators):

装饰者是:

  • 一种表达
  • 评估函数
  • 将目标,名称和属性描述符作为参数
  • 并可选择返回一个属性描述符以安装在目标对象上

它是这样的(高级视图):

客户代码:

@serializable()
class Person {

    constructor(name: string) {
      this._name = name;
    }

    private _name: string;

    @serialize()
    get name() {
      return this._name;
    }

    @serialize('Language')
    get lang() {
      return 'JavaScript';
    }
}
Run Code Online (Sandbox Code Playgroud)

基础设施:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

更新:我通过https://github.com/awerlang/es-decorators将此示例装饰器提取到存储库中

  • @Jacks也许您正在使用类作为键检索,而不是类'原型?我用一个有效的实现更新了我的答案,所以看一看. (2认同)
  • @AndréWerlang非常感谢您的更新.是的,我确实在第一次尝试通过传递类而不是原型来检索值.但是为了验证,我试图打印整个WeakMap,这是不可能的,因为密钥不可枚举(参见[this](http://stackoverflow.com/questions/41164054/typescript-using-decorators-for-custom- JSON序列化)).感谢您在几年后澄清这篇文章:) (2认同)

And*_*ang 8

是的,这是设计的.

如ECMA中所定义的,只有自己的可枚举属性被序列化(stringify()根据Object.keys()其他方式定义).

属性访问器在原型上定义,包括TypeScript和ES6.

并回答你的最后一个问题,这是执行此操作的最有效方法.

除此之外,只有a)为序列化的每个对象部分定义toJSON(),或b)将替换器函数/数组作为第二个参数传递给JSON.stringify().

原型中的白名单属性:

JSON.stringify(instance, Object.keys(instance.constructor.prototype))
Run Code Online (Sandbox Code Playgroud)