我有一个Car类,其中包含一些汽车包含板的信息,现在我想验证属性板,因此我以这种方式使用属性装饰器:
class Car{
@validate
public plate: string;
public model: string;
// extra info
constructor(plate: string, model: string){
this.plate= plate;
this.model = model;
}
toString(): string{
return `Car: ${this.plate} - ${this.model}`;
}
}
然后我具有以下属性装饰器功能:
function validate(target: any, propertyKey: string){
let value = target[propertyKey];
Object.defineProperty(target, propertyKey, {
get: () => value,
set: (newValue) => {
const pattern = /^[A-Z]{2}\s?[0-9]{3}\s?[A-Z]{2}$/;
if(pattern.test(newValue)){
value = newValue;
} else {
console.error('Non valid plate: ', newValue);
//value = undefined;
}
}
})
}
现在,如果我以这种方式测试代码:
const car1 = new Car('IT123UE', 'Car1Model');
console.log('1 - ', car1.toString());
const car2 = new Car('IT000000UE', 'Car2Model');
console.log('2 - ', car2.toString());
我得到:
1 - Car: IT123UE - Car1Model
Non valid plate: IT000000UE
2 - Car: IT123UE - Car2Model <-- why print car1.plate if car2.plate is not valid and this is car2 object?
我已经使用value = undefined解决了;在验证函数内部的else中,但我不知道为什么在car2.plate中获得car1.plate值。 另一个测试,如果我更改顺序:
const car2 = new Car('IT000000UE', 'Car2Model');
console.log('2 - ', car2.toString());
const car1 = new Car('IT123UE', 'Car1Model');
console.log('1 - ', car1.toString());
我得到:
Non valid plate: IT000000UE
2 - Car: undefined - Car2Model <- now is undefinied
1 - Car: IT123UE - Car1Model
我期望什么,但是为什么以前没有奏效?
我将TS 3.4.5与VS Code一起使用,并且在tsconfig中,我有
"target": "es6",
"experimentalDecorators": true,
答案 0 :(得分:1)
如果car2.plate无效并且这是car2对象,为什么打印car1.plate?
简短的答案:原因是两个类实例都在访问相同的类原型属性,并且原型属性是类实例之间的共享状态。
更长的答案:您的validate
函数是实例属性上的property decorator。 TypeScript文档说实例属性装饰器将类原型作为第一个参数。结果,在验证器内部,您正在设置类原型的plate
属性,而不是特定类实例的属性。由于类实例共享类原型,因此第二个类实例访问第一个实例已设置的属性值。
这里是一个演示了两种方法的演示; 第二种方法将为您服务。首先是您最初对共享原型状态所做的操作。第二个(validateToo
)不使用共享状态; get / set而不是对target
进行操作,而对this
进行操作,并且get / set成为箭头函数,而不是箭头函数,因此它们采用正确的this
对象。 / p>
class Car {
@validate
public plate: string;
@validateToo
public plateToo: string;
constructor(plate: string) {
this.plate = plate;
this.plateToo = plate;
}
}
// this second approach will work for you
function validateToo(target: any, propertyKey: string) {
const fieldKey = `_${propertyKey}`;
Object.defineProperty(target, propertyKey, {
get() {
return this[fieldKey];
},
set(newValue) {
// you can put your validation logic here
this[fieldKey] = newValue;
}
})
}
function validate(target: any, propertyKey: string) {
let value = target[propertyKey];
Object.defineProperty(target, propertyKey, {
get: () => value,
set: (newValue) => value = newValue
})
}
演示
const car1 = new Car('one');
const car2 = new Car('two');
const car3 = new Car('three');
console.log(car1.plate, car1.plateToo); // three, one
console.log(car2.plate, car2.plateToo); // three, two
console.log(car3.plate, car3.plateToo); // three, three