تاریخ انتشار: شنبه, ۳ خرداد ۱۳۹۹، ۰۲:۴۹ ب.ظ نویسنده: محمدیان

 

Decorators  یا تزئین کننده‌ها و ReflectDecorators، یکی از پیشنهادهای نگارش بعدی جاوا اسکریپت هستند (ECMAScript 2016) که هم اکنون قابلیت استفاده‌ی از آن‌ها در TypeScript وجود دارد.
جهت افزودن قابلیت‌های 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 { }

و ساده‌ترین فرم پیاده سازی این decoratorExpression به صورت زیر است:

function decoratorExpression(target) {
   // Add a property on target
   target.annotated = true;
}

همانطور که ملاحظه می‌کنید، یک decorator در اصل یک function است که دسترسی به target ایی را که قرار است تزئین شود، میسر می‌کند. این قابلیت بسیار شبیه است به مفهومی به نام attributes و annotations در زبان #C.
در ادامه انواع و اقسام 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() {
}

همانطور که در این مثال مشاهده می‌کنید، چندین decorator را نیز می‌توان به تعاریف یک کلاس اعمال کرد که به آن Decorator Composition نیز می‌گویند.


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

تاریخ انتشار: جمعه, ۲ خرداد ۱۳۹۹، ۱۲:۲۶ ب.ظ نویسنده: محمدیان

مبحث بسیار مهم Generic

موقع نوشتن برنامه یکی از جنبه های مهم، ساخت کامپوننت یا اجزا با قابلیت استفاده مجدد است. مبحث جنریک تضمین میکند که برنامه شما در  طولانی مدت، انعطاف پذیر و مقیاس پذیر باشد. Generic راهی برای ایجاد اجزا (کامپوننت های) قابل استفاده مجدد ارائه میدهد.

* Generic ها روشی را برای کار با مؤلفه ها، آرگومان ها یا پارامتر ها یا هر نوع داده دیگری ارائه میدهند و محدود به یک نوع داده نمیشوند.

* بنابراین کامپوننت ها (اجزا) با انواع مختلف داده ها، میتوانند فراخوانی شوند و مورد استفاده قرار بگیرند.

 

به مثال زیر برای درک ضرورت استفاده از نوع داده Generic توجه نمائید:

 

unction getArray(items:any[]):any[]{
    return new Array().concat(items);
}

let myNumArr = getArray([100,105,99,76,20,23,65]);
let myStrArr = getArray(["Hello","world"]);

myNumArr.push("hi from world");
myStrArr.push(1399);

console.log(myNumArr);
console.log(myStrArr);
//------------------------------------- نتیجه
[ 100, 105, 99, 76, 20, 23, 65, 'hi from world' ]
[ 'Hello', 'world', 1399 ]

نتایج فوق را بررسی کنید.

شاید نخواهیم نوع داده رشته ای درون آرایه عددی قرار بگیرد و یا بر عکس نخواهیم نوع داده عددی درون آرایه رشته ای قرار گیرد.

مثال مفهومی:

function echo<T>(data:T){
    return data;
}

console.log(echo<string>("Mokhtar").length);
console.log(echo<number>(34));

درتابع فوق که یک تابع چنریک شده است.<T> نوع داده ای است که قرار است استفاده شود. و اگر هنگام استفاده از تابع ذکر نشود از T استفاده میشود یعنی نوع داده ورودی تابع.

built-in generics - جنریک های داخلی

بسیاری از اشیا جاوااسکریپت، در تایپ اسکریپت دارای نوع داده جنریک هستند مثلا آرایه ها. مثال:

const testResult :Array<number> = [226,-236,456];
testResult.push(34);
console.log(testResult)

یک مثال دیگر از توابع جنریک به همراه استفاده از آرایه.

function printAll<T>(args:T[]){
    args.forEach(elm=>{
        console.log(elm)
    })
}

printAll<string>(['Apple','Banana'])

نوع داده جنریک - Generic Types

type echoType = <T>(data:T)=>T;

const echo2:echoType = echo;
console.log(echo2<string>("somethings"))

انواع دیگر مثال ها و روش های استفاده

<T>
<T,U>
<T extends یک نوع سفارشی>
<T extends number>
<T extends number|string>

const testType:<T extends ...>
const testType:<T>(data:T)=>T;

<T,U extends number|string>
<T extends number,U extends string>

<T extends U>
<T extends U, U extends number|string>


 

تاریخ انتشار: جمعه, ۲ خرداد ۱۳۹۹، ۱۰:۱۱ ق.ظ نویسنده: محمدیان
تاریخ انتشار: جمعه, ۲ خرداد ۱۳۹۹، ۰۸:۳۰ ق.ظ نویسنده: محمدیان

کلاس abstract حالتی بین کلاس های معمولی و interface است و کلاسی می باشد که غیرقطعی و ناتمام است و باید در سطح فرزندانش تکمیل شود. بنابراین نمیتوان از کلاس ناتمام نمونه برداری کرد و یک شئ ساخت. 

کلاس abstract میتواند، درون خودش خواص و متودهای معمولی داشته باشد و خودش آن را کامل کرده باشد و تعدادی هم متود داشته باشد که با برچسب abstract، وظیفه کامل کردن آن را به کلاس های فرزندش سپرده باشد.

 

تایپ اسکریپت به ما این امکان رو میده که با استفاده از کلمه کلیدی abstract یک کلاس abstract ایجاد کنیم. کلاس های abstract عمدتاً برای وراثت است که در آن کلاس های دیگر ممکن است از آن مشتق شوند.

* توجه : ما نمیتوانیم از کلاس های abstract یک نمونه ایجاد کنیم. این قابلیت را ندارند که از روی آنها آبجکت بسازیم.

* یک کلاس abstract به طور معمول شامل یک یا چند متود abstract یا اعلان های خواص است.

* کلاسی که از یک کلاس abstract، ارث بری میکند یا extend میشود. در واقع قصد گسترش کلاس را دارد و باید تمام متودها و خواص abstract را درون خودش تعریف کند.

در مثال زیر کلاس انتزاعی Person شامل یک متود اتزاعی find و همچنین یک متود معمولی display است. چون متود find یک متود انتزاعی است، پس حتماً باید در کلاس Employee که از کلاس Person مشتق شده، تعریف شود.

کلاس Employee باید تمام متودهای abstract موجود در کلاس Person را پیاده سازی کند و انجام دهد، در غیر اینصورت کامپایلر اعلام خطا میکند.

abstract class Person{
    name:string;
    constructor(name:string){
        this.name = name;        
    }
    display(){
        console.log(this.name);
    }
    abstract find(str:string):Person
}
//-------------------------------------------------
class Employee extends Person{
    empCode:number;
    constructor(name:string,code:number){
        super(name);
        this.empCode = code;
    }
    find(name:string){ // متودی که حتماً باید پیاده سازی میشد
        // مثلاً اجرای درخواست آجاکس برای دریافت کارمند از دیتابیس
        return new Employee(name,1);
    }    
}

let emp:Person = new Employee("Mokhtar",5420);
emp.display();
let emp2:Person = emp.find('Amir');
console.log(emp2.name)

خواص abstract هم دقیقاً همانند متود abstract در صورتی که در کلاس abstract موجود باشند، تعریف و پیاده سازی آنها در کلاس مشتق شده الزامی است.

تاریخ انتشار: جمعه, ۲ خرداد ۱۳۹۹، ۰۷:۵۸ ق.ظ نویسنده: محمدیان

static:

از قابلیت های ES6 است و بنابراین در تایپ اسکریپت نیز وجود دارد.

اعضای استاتیک یک کلاس با استفاده از نام کلاس و یک نقطه قابل دسترسی هستند و نیازی نیست برای دسترسی به آن از روی کلاس نمونه سازی انجام شود.

<className> . <StaticPeroperty>

اعضای استاتیک با کلمه کلیدی static تعریف میشوند.

class Circle{
    static pi:number = 3.14;
}

console.log(Circle.pi);

متودهای استاتیک:

class Circle{
    static pi:number = 3.14;
    static calculateArea(radius:number){
        return this.pi*radius*radius;
    }
}

console.log(Circle.calculateArea(5))

در متود calculateArea با کلمه کلیدی this به خاصیت pi دسترسی پیدا کردیم. میتوانستیم به جای this از نام کلاس یعنی Circle هم استفاده کنیم. استفاده از هر دو روش امکان پذیر است.

this.pi // معمولا وقتی استفاده میشود که هم متود وهم خاصیت، هر دو استاتیک هستند
Circle.pi // معمولا وقتی استفاده میشود که متود استاتیک نیست ولی خاصیت استاتیک است

نکته مهم :

نه کلاس و نه متود سازنده اش نمیتوانند استاتیک باشند.

تصور کنید یک کلاس فقط شامل خواص و متودهای استاتیک باشد، بنابراین نمیتوان از آن کلاس یک شئ ایجاد کرد چون هیچ رفتار خاصی از هیچ نوعی از خودش بروز نمیدهد. در چنین کلاسی بهتر است خواص و متودهای Helper یا کمکی تعریف کنیم که عمومی هستند و به سایر اعضای کلاس های ما وابستگی ندارند.

عموماً متودهای کمکی در یک برنامه به صورت مکرر فراخوانی شده و نیاز است که به سرعت در دسترس باشند.

 

تاریخ انتشار: جمعه, ۲ خرداد ۱۳۹۹، ۰۷:۱۱ ق.ظ نویسنده: محمدیان