Поиск…


Вступление

TypeScript, например ECMA Script 6, поддерживает объектно-ориентированное программирование с использованием классов. Это контрастирует со старыми версиями JavaScript, которые поддерживают только цепочку наследования на основе прототипов.

Поддержка классов в TypeScript аналогична поддержке классов, таких как Java и C #, в том, что классы могут наследовать от других классов, тогда как объекты создаются экземплярами класса.

Также похожие на эти языки классы TypeScript могут реализовывать интерфейсы или использовать дженерики.

Простой класс

class Car {
    public position: number = 0;
    private speed: number = 42;
    
    move() {
        this.position += this.speed;
    }
}    

В этом примере мы объявляем простой класс Car . Класс состоит из трех членов: speed частной собственности, position публичной собственности и move общедоступного метода. Обратите внимание, что каждый участник является общедоступным по умолчанию. Вот почему move() является общедоступным, даже если мы не использовали ключевое слово public .

var car = new Car();        // create an instance of Car
car.move();                 // call a method
console.log(car.position);  // access a public property

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

class Car {
    public position: number = 0;
    protected speed: number = 42;
    
    move() {
        this.position += this.speed;
    }
}  

class SelfDrivingCar extends Car {

    move() {
        // start moving around :-)
        super.move();
        super.move();
    }
}

В этом примере показано, как создать очень простой подкласс класса Car с использованием ключевого слова extends . Класс SelfDrivingCar переопределяет метод move() и использует реализацию базового класса, используя super .

Конструкторы

В этом примере мы используем constructor объявить общественное свойство position и защищенное свойство speed в базовом классе. Эти свойства называются параметрами . Они позволяют нам объявить параметр конструктора и член в одном месте.

Одна из лучших вещей в TypeScript - автоматическое назначение параметров конструктора соответствующему свойству.

class Car {
    public position: number;        
    protected speed: number;

    constructor(position: number, speed: number) {
        this.position = position;
        this.speed = speed;
    }
    
    move() {
        this.position += this.speed;
    }        
}

Весь этот код можно возобновить в одном конструкторе:

class Car {
    constructor(public position: number, protected speed: number) {}
    
    move() {
        this.position += this.speed;
    }        
}

И оба они будут переведены из TypeScript (время разработки и время компиляции) на JavaScript с одинаковым результатом, но значительно сократят количество кода:

var Car = (function () {
    function Car(position, speed) {
        this.position = position;
        this.speed = speed;
    }
    Car.prototype.move = function () {
        this.position += this.speed;
    };
    return Car;
}());

Конструкторы производных классов должны вызывать конструктор базового класса с помощью функции super() .

class SelfDrivingCar extends Car {
    constructor(startAutoPilot: boolean) {
        super(0, 42);
        if (startAutoPilot) {
            this.move();
        }
    }
}

let car = new SelfDrivingCar(true);
console.log(car.position);  // access the public property position

Accessors

В этом примере мы модифицируем пример «Simple class», чтобы разрешить доступ к свойству speed . Атрибуты accesscript позволяют добавлять дополнительный код в getters или seters.

class Car {
    public position: number = 0;
    private _speed: number = 42;
    private _MAX_SPEED = 100
    
    move() {
        this.position += this._speed;
    }
    
    get speed(): number {
        return this._speed;
    }

    set speed(value: number) {
        this._speed = Math.min(value, this._MAX_SPEED);
    }
}

let car = new Car();
car.speed = 120;
console.log(car.speed);  // 100

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

abstract class Machine {
    constructor(public manufacturer: string) {
    }

    // An abstract class can define methods of it's own, or...
    summary(): string {
        return `${this.manufacturer} makes this machine.`;
    }
    
    // Require inheriting classes to implement methods
    abstract moreInfo(): string;
}

class Car extends Machine {
    constructor(manufacturer: string, public position: number, protected speed: number) {
        super(manufacturer);
    }
    
    move() {
        this.position += this.speed;
    }
    
    moreInfo() {
        return `This is a car located at ${this.position} and going ${this.speed}mph!`;
    }
}

let myCar = new Car("Konda", 10, 70);
myCar.move(); // position is now 80
console.log(myCar.summary()); // prints "Konda makes this machine."
console.log(myCar.moreInfo()); // prints "This is a car located at 80 and going 70mph!"

Абстрактные классы - это базовые классы, из которых могут расширяться другие классы. Они не могут быть созданы сами собой (т. Е. Вы не можете сделать new Machine("Konda") ).

Двумя ключевыми характеристиками абстрактного класса в машинописном тексте являются:

  1. Они могут реализовывать свои собственные методы.
  2. Они могут определять методы, которые должны выполнять наследующие классы.

По этой причине абстрактные классы концептуально можно рассматривать как комбинацию интерфейса и класса .

Обезьяна исправляет функцию в существующий класс

Иногда полезно иметь возможность расширять класс новыми функциями. Например, предположим, что строка должна быть преобразована в строку верблюда. Поэтому нам нужно указать TypeScript, что String содержит функцию, называемую toCamelCase , которая возвращает string .

interface String {
    toCamelCase(): string;
}

Теперь мы можем исправить эту функцию в реализацию String .

String.prototype.toCamelCase = function() : string {
    return this.replace(/[^a-z ]/ig, '')
        .replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, (match: any, index: number) => {
            return +match === 0 ? "" : match[index === 0 ? 'toLowerCase' : 'toUpperCase']();
        });
}

Если это расширение String загружено, оно можно использовать следующим образом:

"This is an example".toCamelCase();    // => "thisIsAnExample"

Transpilation

Учитывая класс SomeClass , давайте посмотрим, как TypeScript транслируется в JavaScript.

Исходный код

class SomeClass {

    public static SomeStaticValue: string = "hello";
    public someMemberValue: number = 15;
    private somePrivateValue: boolean = false;

    constructor () {
        SomeClass.SomeStaticValue = SomeClass.getGoodbye();
        this.someMemberValue = this.getFortyTwo();
        this.somePrivateValue = this.getTrue();
    }

    public static getGoodbye(): string {
        return "goodbye!";
    }

    public getFortyTwo(): number {
        return 42;
    }

    private getTrue(): boolean {
        return true;
    }

}

Источник JavaScript

Когда транслируется с использованием TypeScript v2.2.2 , вывод выглядит так:

var SomeClass = (function () {
    function SomeClass() {
        this.someMemberValue = 15;
        this.somePrivateValue = false;
        SomeClass.SomeStaticValue = SomeClass.getGoodbye();
        this.someMemberValue = this.getFortyTwo();
        this.somePrivateValue = this.getTrue();
    }
    SomeClass.getGoodbye = function () {
        return "goodbye!";
    };
    SomeClass.prototype.getFortyTwo = function () {
        return 42;
    };
    SomeClass.prototype.getTrue = function () {
        return true;
    };
    return SomeClass;
}());
SomeClass.SomeStaticValue = "hello";

наблюдения

  • Модификация прототипа класса завершается внутри IIFE .
  • Переменные члена определяются внутри основной function класса.
  • Статические свойства добавляются непосредственно к объекту класса, тогда как свойства экземпляра добавляются к прототипу.


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow