دوشنبه , خرداد ۷ ۱۳۹۷
خانه / مقالات / برنامه نویسی / web developer / Front End / آموزش TypeScript(کلاس ها) – قسمت چهارم
TypeScript چیست؟ - آموزش TypeScript-اینترفیس - آموزش TypeScript(کلاس ها)

آموزش TypeScript(کلاس ها) – قسمت چهارم

کلاس ها در TypeScrip

آموزش TypeScript(کلاس ها) – یکی از ویژگی های بارز TypeScript استفاده از کلاس ها و شی گرایی است البته مبحث کلاس ها در نسخه های جدید جاوا اسکریپت(اکما اسکریپت 6) بطور کامل پشتیبانی می شود.

مقدمه

جاوا اسکریپت  در ورژن های پایین تر خود از توابع و ارث بر مبنای  function برای ساختن شی و شی گرایی استفاده می کند، اما این این ممکن است برای برنامه نویسانی که با مبحث کلاس در شی گرایی آشنا هستند خوشایند نباشد ، در حالیکه کلاسها اجزای عملکرد را به ارث می برند و اشیا از این کلاس ها ساخته می شوند. با شروع از ECMAScript 2015، که با عنوان  به عنوان ECMAScript 6 شناخته می شود، برنامه نویسان جاوا اسکریپت قادر خواهند بود برنامه های خود را با استفاده از این شیوه های مبتنی بر کلاس، عملیات شی گرایی را به راحتی انجام دهند. در TypeScript، به توسعه دهندگان اجازه داده می شود  تا از این تکنیک ها استفاده کنند و آنها را به جاوا اسکریپت متصل کنند که در همه مرورگرها و سیستم عامل های اصلی کار می کند بدون نیاز به نسخه بعدی جاوا اسکریپت.

کلاس ها

برای شروع بیایید با یک کلاس ساده شروع کنیم.

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

let greeter = new Greeter("world");

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

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

در خط آخر مثال بالا ما یک نمونه از کلاس بالا با کلمه کلیدی new  ایجاد کرده ایم

این به سازندهی که قبلا در type Scriptتعریف شده است ، ایجاد یک نمونه  جدید با از greeting و اجرای سازنده برای راه اندازی آن می شود.

وراثت

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

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,protected در کلاس ها

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.

accessors دسترسی  ها

تایپ اسکریپت از getters/setters به عنوان راهی برای حائل شدن به دسترسی به اعضای یک شی پشیبانی می کند.این به شما اجازه میدهد که کنترل بهتری به نحوه دسترسی به اعضای اشیا بدهید.

ابتدا برای مثال با یک کلاس ساده get و set آشنا شویم.

class Employee {
    fullName: string;
}

let employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
    console.log(employee.fullName);
}

در حالی که اجازه دادن به کاربر برای تنظیم مستقیم FullName به صورت دستی زیبا به نظر می رسد، اگر کاربر نام ها را به صورت ناگهانی تغییر دهند، برای ما ایجاد دردسر می کند.

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

let passcode = "secret passcode";

class Employee {
    private _fullName: string;

    get fullName(): string {
        return this._fullName;
    }

    set fullName(newName: string) {
        if (passcode && passcode == "secret passcode") {
            this._fullName = newName;
        }
        else {
            console.log("Error: Unauthorized update of employee!");
        }
    }
}

let employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
    console.log(employee.fullName);
}

Static Properties

تا به اینجا تنها در مورد اعضای یک نمونه از کلاس صحبت کردیم، زمانی که نمونه ای از شی ساخته می شود، نشان داده می شود. هم چنین می توانین اعضای ایستا از کلاس را ایجاد کنیم که تنها درون کلاس و نه در نمونه ها نمایان هستند . در این مثال، یک مقدار عمومی در تمامی گرید ها بیان می کنیم.  هر نمونه به این مقدار(ایستا) در زمان prepending کلاس دسترسی پیدا می کند. همان طور که برای دسترسی به نمونه کلاس که از this. استفاده می کردیم، در اینجا از پیشوند .Grid  در ابتدای دسترسی  به اعضای استایتک باید استفاده کنیم.

class Grid {
    static origin = {x: 0, y: 0};
    calculateDistanceFromOrigin(point: {x: number; y: number;}) {
        let xDist = (point.x - Grid.origin.x);
        let yDist = (point.y - Grid.origin.y);
        return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
    }
    constructor (public scale: number) { }
}

let grid1 = new Grid(1.0);  // 1x scale
let grid2 = new Grid(5.0);  // 5x scale

console.log(grid1.calculateDistanceFromOrigin({x: 10, y: 10}));
console.log(grid2.calculateDistanceFromOrigin({x: 10, y: 10}));

Abstract Classes

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

abstract class Animal {
    abstract makeSound(): void;
    move(): void {
        console.log("roaming the earth...");
    }
}

متد هایی که در کلاس های انتزاعی (abstract)  به صورت انتزاعی نشان گذاری شده اند،نباید  پیاده سازی داشته باشند و  باید در کلاس های مشتق شده پیاده سازی شوند.نحوه به اشتراک گذاری متدهای انتزاعی (از نظر شیوه نوشتار) بسیار شبیه به متدهای اینترفیس است.هر دو، ساختار متد ها را بیان می کنند بدون اینکه شامل بدنه متد باشند. اگر چه متدهای انتزاعی باید شامل کلمه کلید abstract  و ممکن است به صورت اختیاری شامل نحوه دسترسی نیز باشند.

abstract class Department {

    constructor(public name: string) {
    }

    printName(): void {
        console.log("Department name: " + this.name);
    }

    abstract printMeeting(): void; // must be implemented in derived classes
}

class AccountingDepartment extends Department {

    constructor() {
        super("Accounting and Auditing"); // constructors in derived classes must call super()
    }

    printMeeting(): void {
        console.log("The Accounting Department meets each Monday at 10am.");
    }

    generateReports(): void {
        console.log("Generating accounting reports...");
    }
}

let department: Department; // ok to create a reference to an abstract type
department = new Department(); // error: cannot create an instance of an abstract class
department = new AccountingDepartment(); // ok to create and assign a non-abstract subclass
department.printName();
department.printMeeting();
department.generateReports(); // error: method doesn't exist on declared abstract type

Advanced Techniques

Constructor functions

زمانی که شما کلاسی را در تایپ اسکریپت تعریف می کنید،در واقع شما چندین تعریف را همزمان انجام می دهید. اول از همه بیان نوع نمونه کلاس است.

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

let greeter: Greeter;
greeter = new Greeter("world");
console.log(greeter.greet());

در اینجا، زمانی که شما let greeter: Greeter را بیان می کنید، از Greerter به عنوان نمونه از کلاس Greeter استفاده می کنید.

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

let Greeter = (function () {
    function Greeter(message) {
        this.greeting = message;
    }
    Greeter.prototype.greet = function () {
        return "Hello, " + this.greeting;
    };
    return Greeter;
})();

let greeter;
greeter = new Greeter("world");
console.log(greeter.greet());

در اینجا، let Greeter در حال اختصاص داده شدن به تابع سازنده است. زمانی که ما new را صدا می زنیم و این تابع را اجرا می کنیم، یک نمونه از کلاس را دریافت می کنیم. تابع سازنده هم چنین تمامی اعضای استاتیک از کلاس را شامل می شود. برای هر کلاس، وجه نمونه و وجه استاتیک وجود دارد.

بیاید این مثال را کمی تغییر بدهیم تا این تفاوت ها مشخص شوند.

class Greeter {
    static standardGreeting = "Hello, there";
    greeting: string;
    greet() {
        if (this.greeting) {
            return "Hello, " + this.greeting;
        }
        else {
            return Greeter.standardGreeting;
        }
    }
}

let greeter1: Greeter;
greeter1 = new Greeter();
console.log(greeter1.greet());

let greeterMaker: typeof Greeter = Greeter;
greeterMaker.standardGreeting = "Hey there!";

let greeter2: Greeter = new greeterMaker();
console.log(greeter2.greet());

در این مثال greeter1 همانند قبل عمل می کند.ما کلاس  greeter را معرفی می کنیم و از این شی استفاده می کنیم. این چیزی است که قبلا دیدید.

سپس، ما کلاس را به صورت مستقیم استفاده می کنیم. در اینجا ما متغیر جدید ایجاد می کنیم که greeterMaker نامیده می شود. این متغیر کلاس را درون خودش نگه می دارد، یا به روش دیگر تابع سازنده اش را صدا می زدند. در اینجا ما typeof Greeter  را استفاده کردیم که نوع کلاس greeter خودش را به جای نوع نمونه می دهد. دقیقا نوع فراخوانی شده greeter را می دهد، که نوع تابع سازنده است. این نوع تمامی اعضای استاتیک از greeter را همراه با سازنده که نمونه کلاس greeter را ایجاد می کند را شامل می شود.ما این کار را با استفاده از new  در  greeterMaker نشان دادیم (با ایجاد کردن نمونه کلاس Greeter  و فراخوانی آن ها همانند قبل)

استفاده از کلاس به عنوان اینترفیس

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

class Point {
    x: number;
    y: number;
}

interface Point3d extends Point {
    z: number;
}

let point3d: Point3d = {x: 1, y: 2, z: 3};

ترجمه مقاله Classes . TypeScriopt

آموزش TypeScript(کلاس ها)

  • آیا این مقاله مفید بود؟
  • بله   خیر

با تمام وجود علاقمندم مباحث جدید مربوط به برنامه نویسی رو یاد بگیرم و به دیگران یاد بدم. نیمی از زمان روزانه رو صرف یادگیری میکنم. سعی میکنم مقالات مفید و جدید در حوزه برنامه نویسی و به ویژه جاوا اسکریپت رو برای شما دوستان عزیز در لایو گوگل منتشر کنم.

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد.