[英]Typescript / Javascript custom Property decorators
I've just started to learn in more depth Typescript and ES6 capabilities and I have some misunderstandings regarding how you should create and how it actually works a custom Property Decorator. 我刚刚开始更深入地学习Typescript和ES6功能,并且对如何创建自定义的Property Decorator以及它如何实际工作有一些误解。
This are the sources that I'm following Source1 Source2 这是我关注的资源Source1 Source2
And this is my custom deocrator. 这是我的自定义除草剂。
export const Required = (target: Object, key: string) => {
let value: any = target[key];
const getter = () => {
if (value !== undefined) return value;
throw new RequiredPropertyError(MetadataModule.GetClassName(target), key, ErrorOptions.RequiredProperty)
}
const setter = (val) => value = val;
if (delete this[key]) {
Object.defineProperty(target, key, {
get: getter,
set: setter,
enumerable: true,
configurable: true,
});
}
}
It is applied like so 像这样应用
export classMyClass{
@Required
public Items: number[];
}
What I don't understand is why it works differently then what would you expect. 我不明白的是为什么它的工作原理与您期望的不同。 Well it works but I don't know if it works accordingly to the let's call it "decorator philosophy", 很好,但是我不知道它是否适合我们称之为“装饰者理念”的作品,
Let me explain what I don't understand 让我解释一下我不明白的
Starting with the first line of code. 从第一行代码开始。
let value: any = target[key];
I would expect that the value
would be initialized with Items
value, but instead is undefined
, why? 我希望该value
将使用Items
值初始化,但undefined
,为什么? how? 怎么样? i really don't understand. 我真的不明白。
I've followed both sources and that first thing that I find it confusing is the fact that one used target[key]
to initialize the value
while the other used this[key]
, shouldn't this
refer to the Required actually. 我一直关注这两个资源,发现令我感到困惑的第一件事是,一个人使用target[key]
来初始化value
而另一个人使用了this[key]
, this
实际上不应该引用Required。
What I also find confusing is this part 我也感到困惑的是这部分
if (delete this[key]) {
Object.defineProperty(target, key, {
get: getter,
set: setter,
enumerable: true,
configurable: true,
});
}
Firstly, why I need to delete this[key] ? 首先,为什么我需要删除this [key]? from my understanding this should refer to the current object context in my this case Required
, and when debugged it is as so. 根据我的理解,在本例中为Required
,它应指代当前对象的上下文,而在调试时则是如此。
Secondly, that Object.defineProperty will create a property with the name of the key on the target class in my case MyClass
, but isn't this property already there? 其次,在我的案例MyClass
,该Object.defineProperty将在目标类上创建一个具有键名的属性,但是此属性不存在吗?
Moving forward and setting the value
using the setter, how does the setter parameter val
know what data it should hold ? 前进并使用设置器设置value
,设置器参数val
如何知道应保存哪些数据? I mean where is it coming ? 我的意思是它要去哪里?
Thanks all. 谢谢大家
There's a lot of questions in there, but I'll try to answer them all :) 那里有很多问题,但是我会尽力回答所有问题:)
I would expect that the
value
would be initialized withItems
value, but instead isundefined
, why? 我希望该value
将使用Items
值初始化,但undefined
,为什么?
When your MyClass
class is instantiated and your decorator code is run, the value of all properties of the object are undefined
(even if you use a TypeScript property initializer, which you aren't). 当实例化MyClass
类并运行装饰器代码时,该对象的所有属性的值都undefined
(即使您使用的不是TypeScript属性初始化器)。
Shouldn't
this
refer to theRequired
?this
不应该指Required
吗?
Yes, I think it does in your example. 是的,我认为您的示例中确实如此。 But in your Source1 link , the decorator is defined using a function expression ( function logProperty()
); 但是在Source1链接中 ,装饰器是使用函数表达式( function logProperty()
)定义的; in your example, you have switched this to an arrow function expression ( const Required = ( ... ) =>
). 在您的示例中,您已将其切换为箭头函数表达式( const Required = ( ... ) =>
)。 It's likely this switch will change what this
refers to. 此开关可能会更改this
所指的内容。 To be safe, switch this to target[key]
. 为了安全起见,请将其切换为target[key]
。
Why I need to delete this[key]? 为什么我需要删除此[键]?
This block of code is deleting the original Items
property and replacing it with a property of the same name that allows you to spy on the getting and setting of the variable. 此代码块将删除原始的Items
属性,并将其替换为具有相同名称的属性,该属性使您可以监视变量的获取和设置。 The delete this[key]
line is guarding against the case where the property isn't configurable. delete this[key]
行可防止该属性不可配置。 ( The delete
operator returns false
if the property in question is non-configurable: ). ( 如果有问题的属性是不可配置的,则delete
运算符将返回false
。)
Object.defineProperty will create a property with the name of the key on the target class in my case
MyClass
, but isn't this property already there? 在我的案例MyClass
,Object.defineProperty将在目标类上创建一个具有键名的属性,但是此属性不存在吗?
Yes, as mentioned above - this code is replacing the property with a new property. 是的,如上所述,此代码将属性替换为新属性。 The new property is set up to allow you observe the getting and setting of this variable. 设置了新属性,使您可以观察此变量的获取和设置。
Moving forward and setting the
value
using the setter, how does the setter parameterval
know what data it should hold ? 前进并使用设置器设置value
,设置器参数val
如何知道应保存哪些数据? I mean where is it coming ? 我的意思是它要去哪里?
When your new Items
property is set, the setter
function is called with the soon-to-be new value of the property as a parameter (this setter
function was previously set up as a setter function using Object.defineProperty
). 当你的新Items
属性设置,该setter
函数被调用的财产即将被新的值作为参数(这个setter
功能以前设置为使用setter函数Object.defineProperty
)。 The implementation of this setter
function sets value
, which is a reference to target[key]
. 此setter
函数的实现将设置value
,该value
是对target[key]
的引用。 As a result, the value of Items
gets updated. 结果, Items
的值被更新。
To better understand how all this is working, try adding in some logging statements and then play around with getting/setting the Items
property: 为了更好地理解所有这些是如何工作的,请尝试添加一些日志记录语句,然后尝试获取/设置Items
属性:
export const Required = (target: Object, key: string) => {
let value: any = target[key];
console.log('Initialize the Required decorator; value:', value);
const getter = () => {
console.log('Inside the getter; value is:', value);
if (value !== undefined) return value;
throw new RequiredPropertyError(MetadataModule.GetClassName(target), key, ErrorOptions.RequiredProperty);
};
const setter = val => {
console.log('Inside the setter; val is: ', val, 'value is:', value);
return (value = val);
};
if (delete this[key]) {
console.log('Replacing the "' + key + '" property with a new, configured version');
Object.defineProperty(target, key, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
}
};
export class MyClass {
@Required
public Items: number[];
}
// instantiated your class
var mc = new MyClass();
// set the Items property
mc.Items = [4, 5, 6];
// get the value with no Errors
mc.Items;
// set the Items property to undefined
mc.Items = undefined;
// get the value - an Error is thrown
mc.Items;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.