为什么在装饰器中使用 Object.defineProperty 创建的 属性 没有显示在对象上?
Why does a property created with Object.defineProperty in a decorator not show up on the object?
我想通过在 class 定义中添加装饰器来验证设置 属性 的值。
function Test() {
return (target: object, key: string) => {
let val = '';
Object.defineProperty(target, key, {
get: () => val,
set: (v: string) => {
if(/.*\s.*/.test(v)) throw new Error(`Id '${v}' must not contain any whitespace!`)
val = v;
},
enumerable: true
});
}
}
class FooClazz {
@Test()
id: string;
constructor(id: string) {
this.id = id;
}
}
const test = new FooClazz("myId");
console.log(Object.keys(test)) // -> [];
setter 验证程序有效,但是当我尝试注销对象或其键时,id
没有出现。即使我将 enumerable
设置为 true
。
我做错了什么?
在这种情况下,您将 属性 添加到 FooClazz
(而不是添加到 FooClazz
的实例,例如 test
),它不会对其执行任何操作实例。
为了确保 test
,FooClazz
的一个实例,得到 属性,你需要在 FooClazz
的原型上定义它:
function Test() {
return (target: any, key: string) => {
target.prototype = target.prototype || {};
let val = '';
Object.defineProperty(target.prototype, key, {
get: () => val,
set: (v: string) => {
if(/.*\s.*/.test(v)) throw new Error(`Id '${v}' must not contain any whitespace!`)
val = v;
},
enumerable: true
});
}
}
class FooClazz {
@Test()
id: string;
constructor(id: string) {
this.id = id;
}
}
const test = new FooClazz("myId");
console.log(Object.keys(test));
问题是Test
装饰器装饰了FooClazz
class的prototype
属性。但是您正在尝试获取 FooClazz
实例的 own 键。虽然 id
属性 存在于它的原型链上:
console.log(Object.keys(test.__proto__)) // -> ["id"];
另一个问题是 Test
装饰器只运行一次(当装饰 FooClazz
的原型时)并且 val
变量在实例中是 'shared' (尽管它可能是故意的?)。
const test = new FooClazz("myId");
const anotherTest = new FooClazz("anotherId");
console.log(test.id); // -> "anotherId"
console.log(anotherTest.id); // -> "anotherId"
您可以使用一个技巧来重新定义第一组实例本身的键:
function Test(target: any, key: string) {
Object.defineProperty(target, key, {
get: () => '',
set: function(v: string) {
var val = '';
Object.defineProperty(this, key, {
get: () => val,
set: (v: string) => {
if(/.*\s.*/.test(v)) throw new Error(`Id '${v}' must not contain any whitespace!`)
val = v;
},
enumerable: true,
});
this[key] = v;
},
});
}
class FooClazz {
@Test
id: string;
constructor(id: string) {
this.id = id;
}
}
我想通过在 class 定义中添加装饰器来验证设置 属性 的值。
function Test() {
return (target: object, key: string) => {
let val = '';
Object.defineProperty(target, key, {
get: () => val,
set: (v: string) => {
if(/.*\s.*/.test(v)) throw new Error(`Id '${v}' must not contain any whitespace!`)
val = v;
},
enumerable: true
});
}
}
class FooClazz {
@Test()
id: string;
constructor(id: string) {
this.id = id;
}
}
const test = new FooClazz("myId");
console.log(Object.keys(test)) // -> [];
setter 验证程序有效,但是当我尝试注销对象或其键时,id
没有出现。即使我将 enumerable
设置为 true
。
我做错了什么?
在这种情况下,您将 属性 添加到 FooClazz
(而不是添加到 FooClazz
的实例,例如 test
),它不会对其执行任何操作实例。
为了确保 test
,FooClazz
的一个实例,得到 属性,你需要在 FooClazz
的原型上定义它:
function Test() {
return (target: any, key: string) => {
target.prototype = target.prototype || {};
let val = '';
Object.defineProperty(target.prototype, key, {
get: () => val,
set: (v: string) => {
if(/.*\s.*/.test(v)) throw new Error(`Id '${v}' must not contain any whitespace!`)
val = v;
},
enumerable: true
});
}
}
class FooClazz {
@Test()
id: string;
constructor(id: string) {
this.id = id;
}
}
const test = new FooClazz("myId");
console.log(Object.keys(test));
问题是Test
装饰器装饰了FooClazz
class的prototype
属性。但是您正在尝试获取 FooClazz
实例的 own 键。虽然 id
属性 存在于它的原型链上:
console.log(Object.keys(test.__proto__)) // -> ["id"];
另一个问题是 Test
装饰器只运行一次(当装饰 FooClazz
的原型时)并且 val
变量在实例中是 'shared' (尽管它可能是故意的?)。
const test = new FooClazz("myId");
const anotherTest = new FooClazz("anotherId");
console.log(test.id); // -> "anotherId"
console.log(anotherTest.id); // -> "anotherId"
您可以使用一个技巧来重新定义第一组实例本身的键:
function Test(target: any, key: string) {
Object.defineProperty(target, key, {
get: () => '',
set: function(v: string) {
var val = '';
Object.defineProperty(this, key, {
get: () => val,
set: (v: string) => {
if(/.*\s.*/.test(v)) throw new Error(`Id '${v}' must not contain any whitespace!`)
val = v;
},
enumerable: true,
});
this[key] = v;
},
});
}
class FooClazz {
@Test
id: string;
constructor(id: string) {
this.id = id;
}
}