我试图实现一个覆盖属性(1)并定义隐藏属性(2)的装饰器。假设以下示例:
comboBox1.DataSource = ds.Tables[0].Copy();
哪个输出:
function f() {
return (target: any, key: string) => {
let pKey = '_' + key;
// 1. Define hidden property
Object.defineProperty(target, pKey, {
value: 0,
enumerable: false,
configurable: true,
writable: true
});
// 2. Override property get/set
return Object.defineProperty(target, key, {
enumerable: true,
configurable: true,
get: () => target[pKey],
set: (val) => {
target[pKey] = target[pKey] + 1;
}
});
};
}
class A {
@f()
propA = null;
propB = null;
}
let a = new A();
console.log(Object.keys(a), a.propA, a._propA, a);
但是,我宁愿期待:
[ 'propB' ] 1 1 A { propB: null }
[ 'propA', 'propB' ] 1 1 A { propA: 1, propB: null }
enumerable
为true
。
现在,如果我用
替换propA
和get
set
输出现在是:
get: function () {
return this[pKey]
},
set: function (val) {
this[pKey] = this[pKey] + 1;
}
虽然[ '_propA', 'propB' ] 1 1 A { _propA: 1, propB: null }
中enumerable
的{{1}}明确设置为false
。
所以,尽管这些行为可能很奇怪,但我想了解这里发生了什么,以及我将如何实现我想要实现的目标?
答案 0 :(得分:2)
好吧,所以我花了一段时间,但我找到了一个解决方法。问题似乎是Object.defineProperty
在装修时不能正常工作。如果你在运行时这样做,事情会按预期进行。那么,如何在装饰器中定义属性,但在运行时?
这是诀窍:因为覆盖装饰器内的属性在装饰时工作(只有可枚举的行为似乎被破坏),你可以定义属性但使用初始化函数代替getter
和{ {1}}。该函数将在第一次分配属性(setter
)或访问(set
)时运行。当发生这种情况时,单词get
引用对象的运行时实例,这意味着您可以在装饰时正确初始化您打算做的事情。
以下是解决方案:
this
哪个输出:
function f() {
return (target: any, key: string) => {
let pKey = `_${key}`;
let init = function (isGet: boolean) {
return function (newVal?) {
/*
* This is called at runtime, so "this" is the instance.
*/
// Define hidden property
Object.defineProperty(this, pKey, {value: 0, enumerable: false, configurable: true, writable: true});
// Define public property
Object.defineProperty(this, key, {
get: () => {
return this[pKey];
},
set: (val) => {
this[pKey] = this[pKey] + 1;
},
enumerable: true,
configurable: true
});
// Perform original action
if (isGet) {
return this[key]; // get
} else {
this[key] = newVal; // set
}
};
};
// Override property to let init occur on first get/set
return Object.defineProperty(target, key, {
get: init(true),
set: init(false),
enumerable: true,
configurable: true
});
};
}
此解决方案支持默认值,因为它们是在初始化正确的get / set之后分配的。
它还正确支持[ 'propA', 'propB' ] 1 1 A { propA: [Getter/Setter], propB: null }
:将enumerable
设置为enumerable
以获取属性true
,输出将为:
pKey
它并不漂亮,我知道,但据我所知,它可以工作并且不会降低性能。
答案 1 :(得分:0)
我检查了您的代码,发现它将定义该属性两次。我修改了您的代码。
class A {
@dec
public value: number = 5
}
function dec(target, key) {
function f(isGet: boolean) {
return function (newValue?: number) {
if (!Object.getOwnPropertyDescriptor(this, key)) {
let value: number;
const getter = function () {
return value
}
const setter = function (val) {
value = 2 * val
}
Object.defineProperty(this, key, {
get: getter,
set: setter,
enumerable: true,
configurable: true
})
}
if (isGet) {
return this[key]
} else {
this[key] = newValue
}
}
}
Object.defineProperty(target, key, {
get: f(true),
set: f(false),
enumerable: false,
configurable: false
})
}
const a = new A()
console.log(Object.keys(a))
我们将进入控制台
["value"]