JSON.stringify(),只序列化对象自己的可枚举属性。使用该装饰器,您可以将 getter 添加为原型对象的属性。
来自ES6 Spec:
24.3.2.3 运行时语义:SerializeJSONObject(值)
带有参数值的抽象操作SerializeJSONObject
序列化一个对象。它可以访问堆栈、缩进、间隙和
当前调用 stringify 方法的 PropertyList 值。
(...)
- 如果 PropertyList 不是未定义的,则
- 否则,
相关点是6。
如果你问,PropertyList 与serialization of arrays 相关,所以它不可能是逃生舱。
使用方法级装饰器,虽然你说你不想要它,但我能想到的唯一方法是让装饰器在类的原型中添加(如果不存在)toJSON。
这样的toJSON 将(仅在第一次调用时)将类原型中的getter(作为可枚举属性)添加到调用toJSON 的实例中。
棘手的部分是,当您装饰了多个 getter 时,在处理第二个和 on 装饰器时,区分您拥有的 toJSON 是否由您创建(然后您可以“附加”一个属性当前的装饰器)或者它最初是否在类中(在这种情况下你会忽略)。棘手,但据我所知,可行。
演示
根据要求,您就可以了。 JSFiddle demo here.
class MyClass {
constructor(public name) {}
@enumerable(true)
get location(): string {
return 'Hello from Location!'
}
@enumerable(true)
get age(): number {
return 12345;
}
get sex(): string {
return "f";
}
}
class MyClassWithToJson {
constructor(public name) {}
@enumerable(true)
get nickname(): string {
return 'I shall still be non enumerable'
}
toJSON() { return 'Previously existing toJSON()!' }
}
function enumerable(value: boolean) {
return function (target: any, propertyKey: string) {
if (!value) {
// didnt ask to enumerate, nothing to do because even enumerables at the prototype wont
// appear in the JSON
return;
}
if (target.toJSON && !target.__propsToMakeEnumerable) {
return; // previously existing toJSON, nothing to do!
}
target.__propsToMakeEnumerable = (target.__propsToMakeEnumerable || []).concat([{propertyKey, value}])
target.toJSON = function () {
let self = this; // JSFiddle transpiler apparently is not transpiling arrow functions properly
if (!this.__propsToMakeEnumerableAlreadyProcessed) { // so we just do this once
console.log('processing non-enumerable props...'); // remove later, just for testing
let propsToMakeEnumerable = self.__propsToMakeEnumerable;
(propsToMakeEnumerable || []).forEach(({propertyKey, value}) => {
let descriptor = Object.getOwnPropertyDescriptor(self.__proto__, propertyKey);
descriptor.enumerable = true;
Object.defineProperty(self, propertyKey, descriptor);
});
Object.defineProperty(this, '__propsToMakeEnumerableAlreadyProcessed', {value: true, enumerable: false});
}
return this;
}
};
}
let obj = new MyClass('Bob');
console.log(JSON.stringify( obj ));
console.log(JSON.stringify( obj )); // this second time it shouldn't print "processing..."
console.log(JSON.stringify( new MyClassWithToJson('MyClassWithToJson') ));
更新TypeScript playground link here。