چهارشنبه, 22 آذر 1396

آموزش تایپ اسکریپت

همه چیز در باره تایپ اسکریپت

کلاس ها در تایپ اسکریپت(#1)

کلاس ها

معرفی

در جاوااسکریپت مرسوم، از توابع و prototype برای ساختن کامپوننت های قابل استفاده مجدد استفاده می شد، اما این مورد برای برنامه نویسانی که با رویکرد شی گرایی راحت بودند (که در آن می توان کلاس هایی که از توابع و اشیا کلاس های دیگر ارث بری می کنند، استفاده کنیم) کمی غیر ماهرانه به نظر می رسید. با شروع ECMAScript 2015 که با نام متداول ECMAScript 6 شناخته شده،  برنامه نویسان جاوا اسکریپت قادر به ساخت برنامه های کاربردی خود با استفاده از این رویکرد مبتنی بر کلاس شی گرا خواهند بود. در تایپ اسکریپت، به توسعه دهندگان اجازه می دهد که از تکینیک های جدید استفاده کنند و آن ها را به جاوااسکریپتی که در تمامی مرورگرهای اصلی و سیستم عامل کار می کنند، کامپایل کنند بدون اینکه بخواهند منتظر آمدن نسخه جدیدی از جاوا اسکریپت باشند.

کلاس ها

اجازه دهید نگاهی به یک مثال ساده بزنیم:

class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}

let greeter = new Greeter("webnegar");

اگر قبلا شما #C و یا جاوا استفاده کرده باشید، این نحو نوشتن آشنا می باشد. این کلاس این اعضا را دارد، یک ویژگی که greetin نامیده شده، یک سازنده و یک متد به نام greet.

 شما متوجه خواهید شد که هنگامی که به یک عضو از کلاس اشاره می کنید از کلمه .this  استفاده می کنیم. نشان دهنده آن است که این دسترسی به عضو است.  در خط آخر، ما یک نمونه از کلاس Greeter را با استفاه از کلمه new ایجاد کردیم.این فراخوانی سازنده که قبلا معرفی کردیم، یک شی جدید از Greeter می سازد و سازنده را برای مقداردهی اولیه آن اجرا می کند.

وراثت

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

class Animal {
    name: string;
    constructor(theName: string) { this.name = theName; }
    move(distanceInMeters: number = 0) {
        console.log(`${this.name} moved ${distanceInMeters}m.`);
    }
}

class Snake extends Animal {
    constructor(name: string) { super(name); }
    move(distanceInMeters = 5) {
        console.log("Slithering...");
        super.move(distanceInMeters);
    }
}

class Horse extends Animal {
    constructor(name: string) { super(name); }
    move(distanceInMeters = 45) {
        console.log("Galloping...");
        super.move(distanceInMeters);
    }
}

let sam = new Snake("Sammy the Python");
let tom: Animal = new Horse("Tommy the Palomino");

sam.move();
tom.move(34);

این مثال چند ویژگی وارثت در تایپ اسکریپت را که در زیان های دیگر رایج است، را کاملا پوشش می دهد. در اینجا ما کلمات کلیدی extends برای ایجاد زیرکلاس می بینیم.شما زیرکلاس های Horse و Snake از کلاس اصلی Animal و دسترسی به ویژگی های کلاس اصلی را می بینید. کلاس های مشتق شده که از توابع سازنده تشکیل شده اند باید  ()super را فراخوانی کنند که تابع سازنده در کلاس اصلی را اجرا کند. این مثال هم چنین نشان میدهد که چگونه می توان متدهای کلاس اصلی را با متدهای اختصاصی در زیرکلاس ها بازنویسی کرد (override). در اینجا Snake و Horse هر دو متد move را دارند که با متد move در کلاس Animal بازنویسی شده  است که هر کلاس قابلیت خاصی به آن داده است.توجه داشته باشید که اگر tom به عنوان Animal تعریف شده است، تا زمانی که مقدار آن Horse است وقتی که tom.move(34) صدا زده می شود متد درون Horse فراخوانی می شود.

Slithering...
Sammy the Python moved 5m.
Galloping...
Tommy the Palomino moved 34m.

Public, private, and protected modifiers

Public by default

در مثال هایی که زدیم، ما می توانیم آزادانه به اعضایی که در برنامه تعریف کرده ایم دسترسی پیدا کنیم.اگر شما با کلاس ها در زبان های دیگر آشنا باشید، متوجه خواهید شد که در مثالهای بالا ما لزومی به اسفاده از کلمه public برای انجام کار نداریم.برای نمونه، زبان #C هر عضو برای قابل مشاهده بودن نیاز دارد به صراحت با کلمه public عنوان شده باشد.در زبان تایپ اسکریپت هر عضو به صورت پیش فرض public هست. ممکن هست شما هنوز بخواهید یک عضو را به صراحت public بیان کنید. ما می خواهیم کلاس Animal از بخش های قبل را به صورت زیر بنویسیم:

class Animal {
    public name: string;
    public constructor(theName: string) { this.name = theName; }
    public move(distanceInMeters: number) {
        console.log(`${this.name} moved ${distanceInMeters}m.`);
    }
}

 private

زمانی که یک عضو با private نشانه گذاری می شود، این عضو  در خارج از کلاسی که در آن قرار دارد، قابل دسترسی نیست. به عنوان مثال:

class Animal {
    private name: string;
    constructor(theName: string) { this.name = theName; }
}

new Animal("Cat").name; // Error: 'name' is private;

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

 با یک مثال این مطلب را بهتر بیان کنیم: 

class Animal {
    private name: string;
    constructor(theName: string) { this.name = theName; }
}

class Rhino extends Animal {
    constructor() { super("Rhino"); }
}

class Employee {
    private name: string;
    constructor(theName: string) { this.name = theName; }
}

let animal = new Animal("Goat");
let rhino = new Rhino();
let employee = new Employee("Ehsan");

animal = rhino;
animal = employee; // Error: 'Animal' and 'Employee' are not compatible

در این مثال، ما دو کلاس Animal و Rhino داریم، که Rhino یک زیر کلاس از Animal است. هم چنین یک کلاس Employee داریم که به نظر می رسد که شکل یکسانی با کلاس Animal داشته باشد.ما نمونه ای از این کلاس ها می سازیم و سپس سعی می کنیم به هم دیگر مقداردهی کنیم(assign کنیم) تا ببینم چه اتفاقی خواهد افتاد. به دلیل اینکه کلاس های Animal و Rhino وجه خصوصی شان را از تعریف private name: string در کلاس Animal بخش بندی کردند، آنها سازگار هستند.زمانیکه بخواهیم کلاس Animal را به Employee مقداردهی کنیم(assign کنیم) یک خطا مبتنی بر عدم سازگاری نوع دریافت می کنیم. حتی اگر Employee یک عضو private با اسم name داشته باشد باز هم از آنچه که در کلاس Animal تعریف شده نیست.

protected

 اصلاح protected بسیار شبیه به  private عمل می کند با این استثنا که اعضایی که با protected تعریف شده اند از کلاس هایی که استخراج می شوند نیز قابل دسترسی اند.به عنوان مثال:

class Person {
    protected name: string;
    constructor(name: string) { this.name = name; }
}

class Employee extends Person {
    private department: string;

    constructor(name: string, department: string) {
        super(name);
        this.department = department;
    }

    public getElevatorPitch() {
        return `Hello, my name is ${this.name} and I work in ${this.department}.`;
    }
}

let howard = new Employee("Webnegar", "Academy");
console.log(howard.getElevatorPitch());
console.log(howard.name); // error

توجه داشته باشید که در حالی که ما نمی توانیم از name در خارج از  Person استفاده کنیم،ما می توانیم از آن درون نمونه متد Employee استفاده کنیم چرا که Employee از Person مشتق شده است.  سازنده به عنوان protected نشان گذاری می شود. بدین معنا که کلاس نمی تواند در خارج از کلاس هایی موجود در آن معرفی شود ولی می تواند توسعه پیدا کند. برای مثال:

class Person {
    protected name: string;
    protected constructor(theName: string) { this.name = theName; }
}

// Employee can extend Person
class Employee extends Person {
    private department: string;

    constructor(name: string, department: string) {
        super(name);
        this.department = department;
    }

    public getElevatorPitch() {
        return `Hello, my name is ${this.name} and I work in ${this.department}.`;
    }
}

let howard = new Employee("Webnegar", "Pouya");
let john = new Person("Webnegar"); // Error: The 'Person' constructor is protected

Readonly modifier

شما می توانید با کلمه کلیدی Readonly ویژگی Readonly را استفاده کنید. ویژگی readonly باید در تعریف خود و یا در سازنده مقدار دهی شوند.

 

class Octopus {
    readonly name: string;
    readonly numberOfLegs: number = 8;
    constructor (theName: string) {
        this.name = theName;
    }
}
let dad = new Octopus("Man with the 8 strong legs");
dad.name = "Man with the 3-piece suit"; // error! name is readonly.