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

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

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

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

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

instance2instance3 通过序列化定义的 属性 表现出我所期望的。 (查看 plunkr 输出)。

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

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

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

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

如您所见,它不会序列化在原型上定义的属性。

我知道这并不理想,但另一种选择是:

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

在实例上定义 属性 将序列化 属性。

是的,这是设计使然。

按照 ECMA 的定义,只有 自己的可枚举属性 被序列化(stringify() 根据 Object.keys() 等定义)。

属性 访问器在原型上定义,在 TypeScript 和 ES6 中都是如此。

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

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

来自原型的白名单属性:

JSON.stringify(instance, Object.keys(instance.constructor.prototype))

如果你想推进它,试试 TypeScript 的装饰器提案:

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

装饰器是:

  • 一个表达式
  • 计算为一个函数
  • 将目标、名称和 属性 描述符作为参数
  • 和可选的returns一个属性描述符安装在目标对象上

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

客户代码:

@serializable()
class Person {

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

    private _name: string;

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

    @serialize('Language')
    get lang() {
      return 'JavaScript';
    }
}

基础设施:

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

更新:我将这个示例装饰器提取到 https://github.com/awerlang/es-decorators

的存储库中

在这里放一些东西希望能帮助到别人。 我所做的修复 JSON.stringify 未序列化原型

上定义的属性
var newObject = $.extend(false, {}, orginalObj);

然后我注意到 newObject 具有实例属性而不是原型属性。 我正在使用打字稿并获取访问器。