3. Классы

Классы

JavaScript фокусируется на функцих и наследовании, основанном на прототипах, для разработки многократно используемых компонентов. По этот подход довольно неуклюжий по сравнению с привычным объектоно-ориентированным программированием. На основе ECMAScript 6 следующая версия JavaScript будет позволять программистам применять ООП подход. В TypeScript такой подход можно рпименять уже сейчас и компилировать код в JavaScript, который уже сейчас будет работать на основной массе браузеров.

Классы

Синтаксис описания класса и принцип работы такой же как в C# или Java. Рассмотрим простой пример.
class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}

var greeter = new Greeter("world");
 

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

В TypeScript используется подход классического ООП. Одной из основных возможностей является создание новыйх классов через наследование.
class Animal {
    name:string;
    constructor(theName: string) { this.name = theName; }
    move(meters: number) {
        alert(this.name + " moved " + meters + "m.");
    }
}

class Snake extends Animal {
    constructor(name: string) { super(name); }
    move() {
        alert("Slithering...");
        super.move(5);
    }
}

class Horse extends Animal {
    constructor(name: string) { super(name); }
    move() {
        alert("Galloping...");
        super.move(45);
    }
}

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

sam.move();
tom.move(34);
Этот пример показывает все возможности наследования TypeScript, такие же как и в других языках, в том числе и возможность переопределения методов.

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

По-умолчанию используется модификатор public, а для сокрытия свойства необходимо явно указать модификатор private.

class Animal {
    private name:string;
    constructor(theName: string) { this.name = theName; }
    move(meters: number) {
        alert(this.name + " moved " + meters + "m.");
    }
}

Понимание private

TypeScript это система структурированных типов. Когда сравниваются два типа, независимо от того где и как они описаны и реализованы, если типы всех элементов этих типов совместимы, можно утверждать что сами эти типы совместимы. Но есть особенность с типами, имеющими приватные свойства. Для того, чтобы такие такие типы были совместимы, необходимо, чтобы приватные свойства были одного и того же происхождения.
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; }  
}

var animal = new Animal("Goat");
var rhino = new Rhino();
var employee = new Employee("Bob");

animal = rhino;
animal = employee; //error: Animal and Employee are not compatible
Поскольку у 'Animal' и 'Rhino' приватная часть задекларирована в одном месте, они совместимы. Однако 'Employee' и 'Animal', несмотря на то, что выглядят идентично, не являются совместимыми, так как их приватные части задекларированы в разных метсах.

Свойства параметры

В TypeScript существует удобный способ описывать свойства типа через параметры конструктора.
class Animal {
    constructor(private name: string) { }
    move(meters: number) {
        alert(this.name + " moved " + meters + "m.");
    }
}

Методы доступа

TypeScript поддерживает методы доступа (get/set) как способ перехватывания доступа к свойствам объектов. Ниже показан пример без методов доступа.
class Employee {
    fullName: string;
}

var employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
    alert(employee.fullName);
}
А в этом примере, который работает аналогично, используются методы доступа для контроля доступа к свойству.
var 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 {
            alert("Error: Unauthorized update of employee!");
        }
    }
}

var employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
    alert(employee.fullName);
}
Замечание: Для использования методов доступа необходимо настроить компилятор, чтобы он выдавал ECMAScript 5.

Статические свойства

До сих пор шла речь о свойствах экземпларов классов, которые видны, когда инициализируется объект класса. Но можно использовать и статические свойства классов, которые видны через классе в целом (через тип класса), но через экземпляры. В примере ниже используется статическое свойство origin, единственное для всех экземпларов Grid. Каждый экземпляр получает доступ к статическому свойству через имя класса.
class Grid {
    static origin = {x: 0, y: 0};
    calculateDistanceFromOrigin(point: {x: number; y: number;}) {
        var xDist = (point.x - Grid.origin.x);
        var yDist = (point.y - Grid.origin.y);
        return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
    }
    constructor (public scale: number) { }
}

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

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

Продвинутые техники

Функции конструктора

КогдА описывается класс TypeScript, на самом деле происходит несколько описаний. Первая - это тип экземпляров класса.
class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}

var greeter: Greeter;
greeter = new Greeter("world");
alert(greeter.greet());
В строке 'var greeter: Greeter' Greeter указывает на тип экземплара класса Greeter. Это отличительная особенность от других ООП языков.
Второе что создается - это функция конструктор. Это функция, которая вызывается, когда создается экземпляр класса через 'new'. Чтобы увидеть как это работает на практике, рассмотрим JavaScript код, сгенерированный этим примером.
var Greeter = (function () {
    function Greeter(message) {
        this.greeting = message;
    }
    Greeter.prototype.greet = function () {
        return "Hello, " + this.greeting;
    };
    return Greeter;
})();

var greeter;
greeter = new Greeter("world");
alert(greeter.greet());
Когда вызывается 'new' и запускается функция, создается объект класса. Функция конструктор также содержит все статические свойства класса. Другими словами класс имеет две стороны: тип и экземпляр.
Следующий пример показывает разницу.
class Greeter {
    static standardGreeting = "Hello, there";
    greeting: string;
    greet() {
        if (this.greeting) {
            return "Hello, " + this.greeting;
        }
        else {
            return Greeter.standardGreeting;
        }
    }
}

var greeter1: Greeter;
greeter1 = new Greeter();
alert(greeter1.greet());

var greeterMaker: typeof Greeter = Greeter;
greeterMaker.standardGreeting = "Hey there!";
var greeter2:Greeter = new greeterMaker();
alert(greeter2.greet());
В этом примере 'greeter1' работает как в предыдущем. Инстанциируется класс Greeter, и используется объект этого класса.
Далее класс используется напрямую. Создается переменная 'greeterMaker', которая содержит сам класс, или, другими словами, функцию конструктор. Конструкция 'typeof Greeter' создает объект типа класса, но не объект класса. Этот объект будет содержать все статические свойства класса и функцию конструктора. А далее создается экземпляр класса через 'new greeterMaker()'.

Использование класса как интерфейса

Описание класса делает две вещи: описывающий экземпляры класса тип и функцию конструктора. Так как классы создают типы, можно их использовать таким эе образом как интерфейсы.

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

interface Point3d extends Point {
    z: number;
}

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

Комментариев нет :

Отправить комментарий

Примечание. Отправлять комментарии могут только участники этого блога.