我正在尝试创建一个 @enumerable 装饰器,它将公开通过访问器方法定义的属性。
在类的实例上执行此操作的函数相当简单:
// This works great when called in the class constructor like ```makeEnumerable(this, ['prop1', 'prop2'])```
const makeEnumerable = (what: any, props: string[]) => {
for (const property of props) {
const descriptor = Object.getOwnPropertyDescriptor(what.constructor.prototype, property);
if (descriptor) {
const modifiedDescriptor = Object.assign(descriptor, { enumerable: true });
Object.defineProperty(what, property, modifiedDescriptor);
}
}
};
但是,似乎不可能将其变成装饰器,因为它没有 实例。
// Does not work for Object.keys, Object.getOwnPropertyNames or Object.entries
function enumerable (value: boolean = true): any {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor): any {
if (descriptor) {
Object.assign(descriptor, { enumerable: value });
}
};
}
属性 does 仍然在
for (const x in y)
结构中枚举(奇怪的是),但没有其他地方 - 更糟糕的是,Object.entries
会抛出错误。
这是使用上述函数的示例:
class MyClass {
#privateVal1: any;
#privateVal2: any;
constructor () {
makeEnumerable(this, ['b']);
}
@enumerable(true)
get a () {
return this.#privateVal1;
}
set a (val: any) {
this.#privateVal1 = val;
}
get b () {
return this.#privateVal2;
}
set b (val: any) {
this.#privateVal2 = val;
}
}
const enumerableA = new MyClass();
enumerableA.a = 5;
enumerableA.b = 6;
const keys = [];
for (const key in enumerableA) {
keys.push(key);
}
console.log({
'forin': keys, // ['a', 'b']
'keys': Object.keys(enumerableA), // ['b']
'keys(proto)': Object.keys(Object.getPrototypeOf(enumerableA)), // ['a']
'getOwnPropertyNames': Object.getOwnPropertyNames(enumerableA), // ['b']
'getOwnPropertyNames(proto)': Object.getOwnPropertyNames(Object.getPrototypeOf(enumerableA)), // ['constructor', 'a', 'b']
});
console.log({
'entries': Object.entries(enumerableA), // Error('Cannot read private member #privateVal1 from an object whose class did not declare it');
'entries(proto)': Object.entries(Object.getPrototypeOf(enumerableA)), // Error('Cannot read private member #privateVal1 from an object whose class did not declare it');
});
有没有办法使用装饰器使访问器方法成为可枚举属性?
这没什么奇怪的,你必须了解原型与实例属性
makeEnumerable
在实例上设置可枚举描述符。enumerable
装饰器修改原型级描述符。您期望
Object.keys(enumerableA)
成为 ['a', 'b']
,就像 'forin': keys
一样,但是:
for...in
循环迭代自己的和继承的可枚举属性。Object.keys
仅返回它自己的可枚举属性。查看此 MDN 博客以获取更多信息。 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Enumerability_and_ownership_of_properties#querying_object_properties
for (const key in enumerableA)
['a', 'b']
for...in
循环迭代自己的和继承的可枚举属性。b
和 a
是可枚举的,但级别不同。 b
由 makeEnumerable
作为实例属性进行枚举,而 a
原型属性由 @enumerable
装饰器进行枚举。Object.keys(enumerableA)
['b']
Object.keys
仅列出 自己的可枚举属性。b
通过构造函数中的makeEnumerable
函数成为自己的可枚举属性。
a
仍在原型上,因此被排除在外。Object.keys(Object.getPrototypeOf(enumerableA))
['a']
@enumerable
装饰器修改a
的原型级描述符。b
在原型上是不可枚举的,因为 makeEnumerable
函数仅在实例上可枚举。Object.getOwnPropertyNames(enumerableA)
['b']
b
是实例自己的属性。Object.getOwnPropertyNames(Object.getPrototypeOf(enumerableA))
['constructor', 'a', 'b']
constructor
和b
是不可枚举的,但存在于原型上。Object.entries
会抛出错误Object.entries
访问所有可枚举的自有属性。
Object.entries(enumerableA)
:
b
,因为它是实例上的可枚举属性。访问 b
时,this
的 get b() {...}
上下文是 MyClass { b: [Getter/Setter] }
的实例。Object.entries(Object.getPrototypeOf(enumerableA))
a
,因为 a
是原型上的可枚举属性。this
的get a(){...}
上下文是原型对象({ a: [Getter/Setter] }
),而不是MyClass
您必须了解 typescript 如何处理私有属性。
this
。如果 this
与 own class
不同,则会抛出错误。不,无法直接使用 Typescript 中的装饰器使 实例属性 可枚举,因为 Typescript 中的属性装饰器只能访问实例成员的
class prototype
,而不能访问实例本身。
instance properities
makeEnumerable
函数,就像您用于 b
那样。