جهت افزودن قابلیتهای meta-programming به زبانهای جاوا اسکریپت و TypeScript و همچنین تعریف annotations بر روی کلاسها و اعضای کلاس، میتوان از Decorators استفاده کرد. یک decorator، تعریف ویژهای است که میتواند به تعریف یک کلاس، متد، خاصیت و یا پارامتر «متصل» شود و به صورت expression@ تعریف میگردد. این expression باید قابلیت فراخوانی به صورت یک متد را داشته باشد که در زمان اجرا فراخوانی خواهد شد. از Decorators در طراحی AngularJS 2 زیاد استفاده شدهاست.
نحوهی فعال سازی Decorators با ES 5
این قابلیت فعلا در مرحلهی آزمایش به سر میبرد؛ بنابراین برای فعال سازی آن نیاز است پارامترهای experimentalDecorators و emitDecoratorMetadata را به کامپایلر خط فرمان tsc و یا به خواص کامپایلر در فایل tsconfig.json اضافه کنید:
Command Line: tsc --target ES5 --experimentalDecorators --emitDecoratorMetadata tsconfig.json: { "compilerOptions": { "target": "ES5", "experimentalDecorators": true, "emitDecoratorMetadata": true } }
بررسی انواع Decorators
در اینجا یک مثال ساده از decoratorها را مشاهده میکنید:
// A simple decorator @decoratorExpression class MyClass { }
function decoratorExpression(target) { // Add a property on target target.annotated = true; }
در ادامه انواع و اقسام decoratorهای ممکن را با مثالهایی بررسی خواهیم کرد.
Class Decorator
یک class Decorator، پیش از تعریف یک کلاس اضافه میشود و هدف آن، اعمال تعاریفی به سازندهی کلاس است و از آن میتوان جهت تحت نظر قرار دادن تعاریف کلاس و یا تغییر یا حتی تعویض کلی تعاریف آن استفاده کرد. یک class Decorator را نمیتوان در سایر فایلهای تعاریف، مانند d.ts. قرار داد.
تنها آرگومانی که به یک class Decorator ارسال میشود، سازندهی کلاسی است که به آن اعمال شدهاست. اگر متد class Decorator مقداری را برگرداند، سبب تعویض و جایگزینی تعاریف کلاس با مقدار باگشت داده شده، میشود.
در ادامه دو مثال را از class Decoratorها مشاهده میکنید:
مثال بدون پارامتر
function ClassDecorator( target: Function // The class the decorator is declared on ) { console.log("ClassDecorator called on: ", target); } @ClassDecorator class ClassDecoratorExample { }
اگر به تصویر فوق و خروجی نهایی آن دقت کنید، مشاهده میکنید که یک decorator برای اجرا، نیازی به وهله سازی از آن کلاس ندارد و اجرای آن دقیقا در زمان تعریف کلاس انجام میشود. این اجرا به معنای تزریق کدهای تزئین کننده، به تعاریف سازندهی کلاس تعریف شده، پیش از وهله سازی از آن است.
مثال با پارامتر
function ClassDecoratorParams(param1: number, param2: string) { return function( target: Function // The class the decorator is declared on ) { console.log("ClassDecoratorParams(" + param1 + ", '" + param2 + "') called on: ", target); } } @ClassDecoratorParams(1, "a") @ClassDecoratorParams(2, "b") class ClassDecoratorParamsExample { }
ClassDecoratorParams(2, 'b') called on: function ClassDecoratorParamsExample() { } ClassDecoratorParams(1, 'a') called on: function ClassDecoratorParamsExample() { }
Property Decorator
یک Property Decorator دقیقا پیش از تعریف یک خاصیت اضافه میشود و نباید در سایر فایلهای تعاریف جانبی قرار گیرد. زمانیکه متد expression آن در runtime فراخوانی میشود، دو پارامتر را دریافت خواهد کرد:
الف) برای static member ها، متد سازندهی کلاس و برای instance memberها، prototype کلاس را دریافت میکند.
ب) نام عضو.
اگر متد expression تعریف شده، مقداری را برگرداند، به عنوان Property Descriptor آن عضو بکارگرفته میشود.
در مثال ذیل که متد PropertyDecorator به خاصیت name اعمال شدهاست، دو پارامتر ارسالی به آنرا بهتر میتوان مشاهده کرد:
function PropertyDecorator( target: Object, // The prototype of the class propertyKey: string | symbol // The name of the property ) { console.log("PropertyDecorator called on: ", target, propertyKey); } class PropertyDecoratorExample { @PropertyDecorator name: string; }
PropertyDecorator called on: PropertyDecoratorExample {} name
Method Decorator
یک Method Decorator باید درست پیش از تعریف یک متد، قرارگیرد و نباید در سایر کلاسها تعاریف نوعهای جانبی افزوده شود. هدف از آن میتواند بررسی، مشاهده و تغییر رفتار یک متد باشد. به متد expression آن، سه پارامتر ارسال میشوند:
الف) برای static member ها، متد سازندهی کلاس و برای instance memberها، prototype کلاس را دریافت میکند.
ب) نام عضو
ج) Property Descriptor عضو
اگر متد تعریف شده، خروجی را برگرداند به عنوان Property Descriptor آن متد استفاده خواهد شد.
در مثال ذیل، متد تزئین کنندهای به نام MethodDecorator تعریف شدهاست که سه پارامتر یاد شده را در زمان اجرا دریافت میکند:
function MethodDecorator( target: Object, // The prototype of the class propertyKey: string, // The name of the method descriptor: TypedPropertyDescriptor<any> ) { console.log("MethodDecorator called on: ", target, propertyKey, descriptor); } class MethodDecoratorExample { @MethodDecorator method() { } }
MethodDecorator called on: MethodDecoratorExample { method: [Function] } method { value: [Function], writable: true, enumerable: true, configurable: true }
و مثالی جهت محدود ساختن آن به یک سری متد خاص که یک پارامتر عددی را دریافت میکنند و یک خروجی عددی نیز دارند:
function TypeRestrictedMethodDecorator( target: Object, // The prototype of the class propertyKey: string, // The name of the method descriptor: TypedPropertyDescriptor<(num: number) => number> ) { console.log("TypeRestrictedMethodDecorator called on: ", target, propertyKey, descriptor); } class TypeRestrictedMethodDecoratorExample { @TypeRestrictedMethodDecorator method(num: number): number { return 0; } }
TypeRestrictedMethodDecorator called on: TypeRestrictedMethodDecoratorExample { method: [Function] } method { value: [Function], writable: true, enumerable: true, configurable: true }
و مثالی از تزئین کنندههای متدهای استاتیک یک کلاس که در این حالت، پارامتر target از نوع متد سازندهی کلاس خواهد بود و نه prototype آن:
function StaticMethodDecorator( target: Function, // the function itself and not the prototype propertyKey: string | symbol, // The name of the static method descriptor: TypedPropertyDescriptor<any> ) { console.log("StaticMethodDecorator called on: ", target, propertyKey, descriptor); } class StaticMethodDecoratorExample { @StaticMethodDecorator static staticMethod() { } }
StaticMethodDecorator called on: function StaticMethodDecoratorExample() { } staticMethod { value: [Function], writable: true, enumerable: true, configurable: true }
Parameter Decorator
یک Parameter Decorator باید درست پیش از تعریف یک آرگومان متد، قرارگیرد و نباید در سایر کلاسها تعاریف نوعهای جانبی افزوده شود. به متد expression آن سه پارامتر ارسال میشوند:
الف) برای static member ها، متد سازندهی کلاس و برای instance memberها، prototype کلاس را دریافت میکند.
ب) نام عضو
ج) شماره ایندکس پارامتر مدنظر در متد
از خروجی متد تزئین کننده در اینجا صرفنظر میشود.
در ادامه مثالی را از نحوهی تعریف یک تزئین کنندهی پارامترها را با سه آرگومان ویژهی آن، مشاهده میکنید:
function ParameterDecorator( target: Function, // The prototype of the class propertyKey: string | symbol, // The name of the method parameterIndex: number // The index of parameter in the list of the function's parameters ) { console.log("ParameterDecorator called on: ", target, propertyKey, parameterIndex); } class ParameterDecoratorExample { method(@ParameterDecorator param1: string, @ParameterDecorator param2: number) { } }
ParameterDecorator called on: ParameterDecoratorExample { method: [Function] } method 1 ParameterDecorator called on: ParameterDecoratorExample { method: [Function] } method 0
یک سری مثال تکمیلی