تا قبل از ES 6 در جاوا اسکریپت از توابع جهت ایجاد کامپوننتهایی با قابلیت استفاده مجدد استفاده میشد. این امر برای برنامهنویسانی که با زبانهای OOP آشنایی دارند، شاید چندان خوشایند نباشد. در TypeScript نیز همانند ES 6 امکان استفاده از کلاسها مهیا است.
سازنده (Constructor)
همانطور که مشاهده میکنید یک سازنده شبیه به یک متد است؛ با این تفاوت که برای نام آن از کلمه کلیدی constructor استفاده میشود. در TypeScript برای یک کلاس تنها یک سازنده را میتوانیم داشته باشیم. البته در دیگر زبانهای برنامهنویسی امکان تعریف چندین سازنده را با پارامترهای مختلف برای یک کلاس میتوانید داشته باشید. برای رسیدن به این هدف در TypeScript میتوان از Optional Parameters استفاده کرد. برای ایجاد یک وهله از کلاس فوق میتوانیم به این صورت عمل کنیم:
در کد فوق با استفاده از کلمهی کلیدی new یک وهله از کلاس ReferenceItem را ایجاد کردهایم و در نهایت آن را به متغیری با نام encyclopedia انتساب دادهایم. یعنی در واقع با استفاده از new توانستهایم سازندهی کلاس را فراخوانی کرده و سپس وهلهایی از آن را به متغیر ذکر شده انتساب دهیم.
پراپرتی، متد
برای دسترسی به این پراپرتی میتوانیم از سینتکس نقطه (.) استفاده کنیم. روش دوم برای تعریف یک پراپرتی، ایجاد accessorهای سفارشی است. accessors در واقع توابع getter و setter هستند که به شما در نحوهی get و set کردن یک پراپرتی کمک خواهند کرد:
همانطور که مشاهده میکنید، accessorهایی را برای پراپرتی editor با استفاده از کلمات کلیدی get و set ایجاد کردهایم. این accessorها در واقع توابعی همنام هستند. تابع get همیشه فاقد پارامتر است. میتوانیم برای تابع get نوع برگشتی را نیز تعیین کنیم (به عنوان مثال در کد فوق نوع برگشتی string است). setter نیز باید تنها یک پارامتر از ورودی دریافت کند. همچنین نمیتوانیم برای آن نوع برگشتی را تعیین کنیم. درون بدنهی این accessorها میتوانیم هر نوع کنترلی را بر روی پراپرتی داشته باشیم. برای دسترسی این accessorها نیز باید از سینتکس نقطه (.) استفاده کنیم.
Parameter properties
با کمک Parameter properties میتوانیم به صورت خلاصهتری اینکار را انجام دهیم:
همانطور که مشاهده میکنید اینکار را با افزودن کلمهی کلیدی public به ابتدای پارامتر name انجام دادهایم. در اینحالت دیگر نیازی به تعریف یک پراپرتی اضافی درون کلاس نخواهیم داشت. کامپایلر TypeScript خودش یک پراپرتی را با همین نام ایجاد کرده و مقدار دریافتی از سازنده را برای آن ست خواهد کرد.
Access Modifiers
همانطور که مشاهده میکنید با استفاده از کلمهی کلیدی extends توانستهایم یک sub-class ایجاد کنیم. بنابراین وهلههای کلاس Journal علاوه بر پراپرتیهای خود (در اینجا contributors ) دارای پراپرتی title و همچنین متد printItem نیز هستند. نکتهایی که در اینجا وجود دارد این است که تمامی sub-classها یا کلاسهای مشتق شده باید درون سازندهی خود، تابع super را فراخوانی کنند؛ با اینکار سازندهی کلاس پایه فراخوانی خواهد شد.
با استفاده از super.printItem به کامپایلر TypeScript گفتهایم که تمامی کدهای درون متد printItem در کلاس پایه نیز اجرا شوند. اگر مایل بودید میتوانید از آن صرفنظر کنید.
همانطور که مشاهده میکنید درون یک کلاس abstract میتوانیم متدهای abstract را نیز داشته باشیم؛ یعنی تنها امضای متد را تعیین کرده و پیادهسازی آن را به کلاسهای مشتق شده واگذار کنیم.
در حالت کلی یک کلاس قالبی برای ایجاد اشیاء است. تمامی اشیاء ایجاد شده از این الگو دارای یکسری پراپرتی و متد میباشند. از پراپرتیها جهت تعریف وضعیتها و از متدها جهت تعریف رفتارها استفاده خواهد شد. همچنین مزیت اصلی یک کلاس، کپسولهسازی قابلیتهای یک موجودیت خاص است. همانند دیگر زبانهای شیءگرا، در TypeScript نیز یک کلاس میتواند ویژگیهای زیر را داشته باشد:
- سازنده (constructor)
- پراپرتی، متد
- Access Modifiers
- ارثبری
- کلاسهای Abstract
در ادامه هر کدام از موارد فوق را بررسی خواهیم کرد.
سازنده (Constructor)
از سازندهها جهت مقداردهی وهلههای یک کلاس استفاده میشود. در ادامه یک کلاس جدید را با استفاده از کلمهی کلیدی class ایجاد کردهایم. این کلاس دارای یک سازنده است:
class ReferenceItem { constructor(title: string, publisher?: string) { // perform initialization here } }
let encyclopedia = new ReferenceItem('WorldPedia', 'WorldPub');
پراپرتی، متد
همانند اینترفیسها، کلاسها نیز میتوانند پراپرتی و متد داشته باشند. با این تفاوت که در کلاسها جزئیات پیادهسازی نیز ذکر خواهد شد. در یک کلاس به دو روش متفاوت میتوانیم پراپرتی را تعریف کنیم. روش اول همانند تعریف یک متغیر است. به عنوان مثال در کلاس زیر یک پراپرتی با نام numberOfPages را از نوع عددی تعریف کردهایم:
class ReferenceItem { numberOfPages: number; }
class ReferenceItem { numberOfPages: number; get editor(): string { // custom getter logic goes here, should return a value } set editor(newEditor: string) { // custom setter logic goes here } }
متدها نیز توابعی هستند که درون یک کلاس تعریف میشوند. برای نمونه در کد زیر یک تابع با نام printChapterTitle را تعریف کردهایم که یک پارامتر را از ورودی دریافت کرده و هیچ مقداری را در خروجی بر نمیگرداند:
class ReferenceItem { numberOfPages: number; get editor(): string { // custom getter logic goes here, should return a value } set editor(newEditor: string) { // custom setter logic goes here } printChapterTitle(chapterNum: number): void { // print title here } }
در حالت عادی برای مقداردهی اولیهی پراپرتیها یک شیء میتوانیم یکسری پارامتر را برای سازنده کلاس تعریف کرده و درون سازنده، پراپرتیهای موردنیازمان را مقداردهی کنیم:
class Author { name: string; constructor(authorName: string) { name = authorName; } }
class Author { constructor(public name: string){} }
Static Properties
تاکنون دربارهی اعضای مربوط به هر وهله از کلاسها صحبت کردیم؛ یعنی اعضایی که در زمان وهلهسازی در دسترس خواهند بود. در واقع میتوانیم اعضای استاتیک را نیز برای کلاسها داشته باشیم. منظور از استاتیک این است که مقادیر یک عضوء استاتیک در وهلههای مختلف یک شیء، متفاوت نیست. بلکه یک مقدار آن برای تمامی وهلهها به اشتراک گذاشته خواهد شد:
class Library { constructor(public name: string) {} static description: string = 'A source of knowledge'; } let lib = new Library('New York Public Library'); console.log(lib.name); // available on instances of the class console.log(Library.description);
با استفاده از Access Modifier میتوانیم میدان دید یک پراپرتی و یا یک متد را برای مصرف کنندهی کلاس کنترل کنیم. TypeScript دارای سه Access Modifier است:
public: در حالت پیشفرض تمامی اعضای یک کلاس عمومی (public) هستند. در نتیجه لزومی به ذکر آن برای پراپرتیها و متدها نیست. یک حالت استثناء، استفاده از Parameter properties است. در این حالت باید کلمهی کلیدی public حتماً ذکر شود.
private: برای محدود کردن دسترسی اعضای یک کلاس میتوانید از کلمهی کلیدی private استفاده کنید. در اینحالت مصرف کنندهی کلاس به اعضای خصوصی (private) دسترسی نخواهد داشت.
protected: این modifier نیز شبیه به private عمل میکند، با این تفاوت که توسط subclassهای مربوط به کلاس تعریف شده در آن نیز قابل دسترس است.
Inheritance
منظور از Inheritance یا ارثبری، اشتراکگذاری تعاریف یک کلاس برای یک یا چند sub-class است. فرض کنید یک کلاس با نام ReferenceItem با یکسری اعضای تعریف شده درون آن داریم و میخواهیم دو کلاس مشتق شده را از این کلاس تهیه کنیم. در اینحالت کلاس ReferenceItem کلاس پایه (base class) و کلاسهای مشتق شده از آن sub-class نامیده میشوند. بنابراین وهلههای ایجاد شده از کلاسهای مشتق شده دارای پراپرتیهای کلاس پایه نیز خواهند بود. برای داشتن قابلیت ارثبری در TypeScript میتوانیم به اینصورت عمل کنیم:
class ReferenceItem { title: string; printItem(): void { // print something here } } class Journal extends ReferenceItem { constructor() { super(); } contributors: string[]; }
لازم به ذکر است که میتوان متدهای کلاس پایه را درون کلاسهای مشتق شده، override کرد. برای اینکار کافی است متد موردنظر در کلاس پایه را درون کلاس مشتق شده مجدداً تعریف کرده و منطق موردنظر را درون آن نوشت:
class Journal extends ReferenceItem { constructor() { super(); } printItem(): void { super.printItem(); console.log('message from Journal'); } contributors: string[]; }
Abstract Classes
کلاسهای Abstract یک نوع خاص از کلاسها هستند که نمیتوان آنها را وهلهسازی کرد. یعنی تنها برای تعریف کلاسهای پایه از آنها استفاده خواهد شد. این نوع کلاسها شبیه به اینترفیسها هستند؛ اما ممکن است دارای پیادهسازی نیز باشند. در ادامه یک نمونه از abstract class را مشاهده میکنید:
abstract class ReferenceItem { private _publisher: string; static departement: string = 'Research'; constructor(public title: string, protected year: number) { } printItem(): void { console.log('message from abstract class'); } get publisher(): string { return this._publisher.toUpperCase(); } set publisher(newPublisher: string) { this._publisher = newPublisher; } abstract printCitation(): void; } class Encyclopedia extends ReferenceItem { constructor(newTitle: string, newYear, public edition: number) { super(newTitle, newYear); } printCitation(): void { console.log('message'); } } let test = new Encyclopedia('WorldPerdia', 1900, 10); test.printItem();
اشتراکها
همکاری تیم TypeScript و AngularJS
Orleankka is a functional extension for Microsoft Orleans framework. It provides a message-based API similar to Akka/ProtoActor, carefully layered on top of Microsoft Orleans (that's what in a name). Orleankka is an excellent choice for use-cases which can benefit from composable, uniform communication interface, such as CQRS, event-sourcing, FSM, etc.
نظرات مطالب
نوعهای نال نپذیر در TypeScript
یک نکتهی تکمیلی: اضافه شدن strictPropertyInitialization به TypeScript 2.7
در نگارش 2.7 اگر یک چنین تعریفی را داشته باشید:
خطای زیر را دریافت خواهید کرد:
یک روش برطرف کردن این خطا، نالپذیر تعریف کردن این خاصیت و سپس مقدار دهی اولیهی به آن است:
و یا در مثال زیر میتوان مشخص کرد که baz میتواند undefined هم باشد:
روش دیگر، مقدار دهی اولیه، حین تعریف یک خاصیت است:
و یا میتوان این بررسی را به صورت زیر خاموش کرد:
با استفاده از عملگر ! به کامپایلر اعلام میکنیم که این خاصیت حتما قرار است توسط روشی کمکی مقدار دهی اولیه شود و نال یا بدون مقدار نیست و استفادهی از آن در این کلاس امن است.
در نگارش 2.7 اگر یک چنین تعریفی را داشته باشید:
export class MovieComponent { @Input() movie: Movie; }
Error! Property movie has no initializer and is not assigned directly in the constructor.
@Input() movie: Movie | null = null;
class C { baz: boolean | undefined; }
class C { bar = "hello"; }
class C { foo!: number; ngOnInit() { this.foo = 0; } }
این الگو اجازهی تعریف کردن عملیاتی جدید را برای مجموعهای از شیءها، بدون تغیر دادن ساختار خود شیءها، میدهد. همچنین اجازهی جدا کردن کلاس را از منطقی که کلاس پیاده سازی میکند، به ما میدهد.
عملیات بیشتری میتوانند در شیء Visitor کپسوله سازی شوند. شیءها میتوانند یک متد visit داشته باشند که یک شیء Visitor را دریافت میکند. Visitor میتواند تغییرات مورد نیاز را ایجاد کند و عملیاتی را بر روی شیءهایی که دریافت کردهاست، انجام دهد.
این الگو به توسعه دهندگان این اجازه را میدهد که کتابخانهها (libraries)، فریم ورکها (frameworks) و ... را گسترش دهند.
مثال:
class Visitor { visit(item){} } class BookVisitor extends Visitor { visit(book) { var cost=0; if(book.getPrice() > 50) { cost = book.getPrice()*0.50 } else{ cost = book.getPrice() } console.log("Book name: "+ book.getName() + "\n" + "ID: " + book.getID() + "\n" + "cost: "+ cost); return cost; } } class Book{ constructor(id,name,price){ this.id = id this.name = name this.price = price } getPrice(){ return this.price } getName(){ return this.name } getID(){ return this.id } accept(visitor){ return visitor.visit(this) } } var visitor = new BookVisitor() var book1 = new Book("#1234","lordOftheRings",80) book1.accept(visitor)
در مثال بالا ما یک کتابفروشی داریم. کلاس Book برای نشان دادن یک کتاب در فروشگاه استفاده شدهاست. این کلاس همانند زیر تعریف شدهاست:
class Book{ constructor(id,name,price){ this.id = id this.name = name this.price = price } //code... }
یک کتاب خصوصیات زیر را دارد:
- id
- name
- price
هم چنین شامل توابع زیر میباشد:
getPrice(){ return this.price } getName(){ return this.name } getID(){ return this.id }
متد getPrice ، قیمت را برگشت میدهد، getName ، نام را برگشت میدهد و getID، شناسهی کتاب را برگشت میدهد.
اکنون کتابفروشی یک تخفیف را برای کتابهایی که هزینهی آنها بیشتر از 50 دلار است، معرفی میکند. در ادامه، میخواهیم یک عملیات دیگر را انجام دهیم و تخفیف را بر روی آنها پیاده سازی کنیم. در اینجا از الگوی visitor استفاده خواهیم کرد. ما یک Visitor را معرفی میکنیم که کتابها را بازدید خواهد کرد و قیمت آنها را بهروزرسانی میکند. بنابراین شیءهای کتاب باید تابعی داشته باشند که اجازه دهد visitor، آنها را بازدید (visit) کند و عملیات مد نظر را بر روی آنها انجام دهد. برای این منظور، یک متد به نام accept در کلاس Book تعریف کردهایم:
accept(visitor){ return visitor.visit(this) }
متد accept یک شیء visitor را به عنوان یک آرگومان دریافت میکند و به آن اجازه میدهد که با فراخوانی کردن تابع visit خودش، کتاب جاری را بازدید (visit) کند (this اشاره به کتاب جاری دارد) .
اکنون اجازه دهید نگاهی به کلاس Visitor داشته باشیم:
class Visitor { visit(item){} }
این کلاس، یک تابع به نام visit دارد و itemی را که میخواهد بازدید (visit ) کند، به عنوان پارامتر دریافت میکند. در این سناریو، میخواهیم که کتابها را بازدید (visit ) کنیم. از این رو، در ابتدا یک کلاس را به نام BookVisitor تعریف میکنیم که کلاس Visitor را extend میکند:
class BookVisitor extends Visitor { visit(book) { var cost=0; if(book.getPrice() > 50) { cost = book.getPrice()*0.50 } else{ cost = book.getPrice() } console.log("Book name: "+ book.getName() + "\n" + "ID: " + book.getID() + "\n" + "cost: "+ cost); return cost; } }
تابع visit، قیمت کتابی را که دارد بازدید میکند، بررسی میکند. اگر بزرگتر از 50 باشد، 50 درصد تخفیف را بر روی آن اعمال میکند؛ در غیر این صورت، قیمت به حالت قبلی خودش باقی میماند.
چه زمانی از این الگو استفاده کنیم:
- زمانیکه نیاز است عملیاتی مشابه، بر روی شیءهای متفاوتی از یک data structure انجام شود.
- زمانیکه نیاز است عملیاتی خاص، بر روی شیءهای متفاوتی از data structure انجام شود.
- زمانیکه میخواهید توسعه پذیری را برای کتابخانهها (libraries) یا فریم ورکها (frameworks) اضافه کنید.
نظرات اشتراکها
مقایسه Angular vs React.js vs Vue.js
نظرات اشتراکها
ایجاد کلاس C# معادل برای فایل XML
دریافت اینترفیس TypeScript از روی ساختار Json