TypeScript. Классы

Наконец-таки наследование здорового человека, через классы, а не через ломающие мозг прототипы! Теперь и в JavaScript! Скоро... Но пока в TypeScript. Реализация аналогична ООП в Java или C#.

Пример класса:

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

    fly(hight: number) { 
        console.log(`${this.name} взлетает на ${hight} м.`)
    }
}

let duck = new Bird('Дональд');
duck.fly(500);

Новый класс объявляется с помощью ключевого слова class. У класса есть свой конструктор, поля (name) и методы (fly). Конструктор - функция, которая будет вызвана при создании нового экземпляра класса. Для обращения к полям класса используем this. Для создания экземпляра класса используем оператор new. Все просто.

Наследование

Воспользуемся классом Bird из примера выше и унаследуемся от него в классах Duck и Chicken:

class Duck extends Bird {
    constructor(name: string) { super(name); }
    fly(hight = 10500) {
        console.log('Кря...');
        super.fly(hight);
    }
}

class Chicken extends Bird {
    constructor(name: string) { super(name); }
    fly(hight = 3) {
        console.log('Кудахтыж...');
        super.fly(hight/2);
    }
}

let donald = new Duck('Дональд');
let theSuperChicken: Bird = new Chicken('Cупер Кура');

donald.fly();
theSuperChicken.fly(50);

Наследование осуществляется с помощью ключевого слова extends. Классы "Утки" и "Курицы" переопределяют метод fly родительского класса "Птицы" в меру своих способностей. Для вызова конструктора или метода родительского класса используется super() или super.fly() соответственно.

Модификаторы доступа

public

Все члены класса в TypeScript по умолчанию являются публичными. Доступ к таким полям и методам класса возможен извне. Явно указать публичность члена класса можно с помощью public:

class Bird {
    public name: string;
    public constructor(name: string) {
        this.name = name;
    }
    public fly(hight: number) { 
        console.log(`${this.name} взлетает на ${hight} м.`)
    }
}

private

Если поле или метод класса помечен как private, то доступ к нему не возможен снаружи самого класса:

class Bird {
    private name: string;
    //...
}
let birdName = new Bird('Безымянный').name; // Ошибка: 'name' is private;

protected

Действие модификатора protected схоже с private, но члены класса с таким модификатором могут быть доступны для классов-наследников:

class Bird {
    protected name: string; // защищенное поле 'name'
    constructor(name: string) {
        this.name = name;
    }
    fly(hight: number) { 
        console.log(`${this.name} взлетает на ${hight} м.`)
    }
}

let birdName = new Bird('Безымянный').name; // Ошибка: 'name' is protected;

class Duck extends Bird {
    constructor(name: string) { super(name); }
    fly(hight = 10500) {
        console.log(`${this.name} к взлету готов!`); // получаем доступ к полю 'name'
        super.fly(hight);
    }
}

let donald = new Duck('Дональд');
donald.fly();

У класса может быть protected конструктор. Создать экземпляр этого класса не получится, но его можно унаследовать.

readonly

Свойства "только на чтение" должны быть инициализированы в конструкторе класса или при их объявлении:

class Bird {
    readonly name: string;
    readonly numOfWings = 2;        
    constructor(name: string) {
        this.name = name;
    }
}

let duck = new Bird('Скрудж Макдак'); // Надо вечерком перепройти Утиные истории...
duck.name = 'Дональд'; // Ошибка: 'name' is readonly

Свойства параметров конструктора

Последний пример можно немного сократить, объявив поле 'name' в параметрах конструктора:

class Bird {
    readonly numOfWings = 2;        
    constructor(readonly name: string) { } // поле 'name' будет создано автоматически
}

В транспилированном JavaScript это будет выглядеть так:

var Bird = (function () {
    function Bird(name) {
        this.name = name;
        this.numOfWings = 2;
    }
    return Bird;
}());

Можно не объявлять поле класса отдельной строкой, а можно делать это в параметрах конструктора, указав модификатор доступа (public, private, protected) или readonly. Соответствующее свойство класса будет создано автоматически.

static

Статические свойства класса (объявленные с использованием ключевого слова static) не привязываются к конкретному экземпляру класса, а являются общими для всех. Доступ к таким полям или методам можно получить, используя название класса, где они объявлены (через точку).

class Bird {
    constructor(private name: string) { }
    static numOfWings = 2; 
    static setNumOfWings(num: number) { 
        Bird.numOfWings = num;
    }
    say () {
        console.log(`${this.name}. Крыльев - ${Bird.numOfWings} шт.`);            
    }
}

let duck = new Bird('Утка');
duck.say(); // Утка. Крыльев - 2 шт.

Bird.setNumOfWings(4); // Меняем статическое свойство для всех птиц

let chicken = new Bird('Курица');
chicken.say(); // Курица. Крыльев - 4 шт.

В этом примере мы имеем статическое число крыльев static numOfWings и статический метод для их изменения static setNumOfWings(). Вызывая метод изменения числа крыльев для птиц, получаем изменение для всех экземпляров (уток и кур).

Геттеры и сеттеры

Геттеры (get) и сеттеры (set) в TypeScript - это специальные функции, которые позволяют организовать взаимодействие со свойствами объекта. В них можно реализовать некоторую логику получения и установки свойств.

let owner = true; 

class Duck {
    private _name: string;

    get name(): string {
        return this._name;
    }

    set name(name: string) {
        if (owner) {
            this._name = name;
        }
        else {
            console.log('Ошибка: Только хозяин может давать имя!');
        }
    }
}

let duck = new Duck();
duck.name = 'Черный плащ';
if (duck.name) {
    console.log(duck.name);
}

В примере выше мы организовали доступ к полю name таким образом, что получить имя может любой, а установить - только хозяин.

Абстрактные классы

Что-то среднее между интерфейсами и обычными классами. Создать экземпляр абстрактного класса нельзя. Часть реализации может присутствовать. С помощью ключевого слова abstract обозначаются как сами абстрактные классы, так и их методы, реализация которых в самом классе отсутствует, а должна быть написана в классах-наследниках.

abstract class Bird {
    abstract say(): string;
    fly(): void {
        console.log('Лечу...');
    }
}

Реализация метода say должна быть описана для каждого класса, который будет наследовать класс Bird.

Павел Прудников

Постигающий дзен фулстэк вэб буддизма

Минск, Беларусь

Подписаться на Блог MEAN stack разработчика

Получайте свежие записи прямо на ваш почтовый ящик.

Или подпишитесь на RSS с Feedly!

Комментарии

comments powered by Disqus