TypeScript 基础 类 class

发布于 2024-11-26 14:52:59 字数 8114 浏览 27 评论 0

传统的 JavaScript 程序使用函数和基于原型的继承来创建可重用的组件,现在 JavaScript 程序员将能够使用基于类的面向对象的方式,基于类的继承并且对象是由类构建出来

在 TypeScript 是不允许直接在 constructor 定义变量的,需要在 constructor 上面先声明:

img

这样引发了第二个问题你如果了定义了变量不用也会报错,通常是给个 默认值 或者 进行赋值

img

img

class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message; // 在引用任何一个类成员的时候都用了 this。 它表示我们访问的是类的成员。
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}

let greeter = new Greeter("world");

extends 继承

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

class Dog extends Animal { // Dog 派生类从基类 Animal 中继承了属性和方法。
    bark() {
        console.log('Woof! Woof!');
    }
}

const dog = new Dog();
dog.bark();
dog.move(10);
dog.bark();
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(),它会执行基类的构造函数。 
        // 并且在构造函数里访问 this 的属性之前,一定要调用 super()。
        super(name); 
    }
    // 重写继承自父类的 move 方法
    move(distanceInMeters = 5) {
        console.log("Slithering...");
        super.move(distanceInMeters);
    }
}

class Horse extends Animal {
    constructor(name: string) { 
        super(name); 
    }
    move(distanceInMeters = 45) { // 重写继承自父类的 move 方法
        console.log("Galloping...");
        super.move(distanceInMeters);
    }
}

let sam = new Snake("Sammy the Python");
// 虽然 tom 被声明为 Animal 类型,但因为它的值是 Horse
let tom: Animal = new Horse("Tommy the Palomino");

sam.move();
// Slithering...
// Sammy the Python moved 5m.
tom.move(34); // 调用 tom.move(34) 时,它会调用 Horse 里重写的方法
// Galloping...
// Tommy the Palomino moved 34m.

publicprivateprotected 修饰符

public 公共

public 修饰符可以让你定义的变量在内部访问,也可以在外部访问。如果不写默认就是 public

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; // 错误: 'name' 是私有的.

TypeScript 使用的是结构性类型系统。当比较两种不同的类型时,并不在乎它们从何处而来,如果所有成员的类型都是兼容的,就认为它们的类型是兼容的。

但是,当我们比较带有 privateprotected 成员的类型的时候,情况就不同了。 如果其中一个类型里包含一个 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("Bob");

animal = rhino; // 因为 Animal 和 Rhino 共享了来自 Animal 里的私有成员定义 private name: string,因此它们是兼容的
animal = employee; // 错误: Animal 与 Employee 不兼容.尽管 Employee 里也有一个私有成员 name,但它明显不是 Animal 里面定义的那个

protected 受保护

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("Howard", "Sales");
console.log(howard.getElevatorPitch());// 可以通过 Employee 类的实例方法访问,因为 Employee 是由 Person 派生而来
console.log(howard.name); // 错误,不能在 Person 类外使用 name

构造函数也可以被标记成 protected : 意味着这个类不能在包含它的类外被实例化,但是能被继承。

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

// Employee 能够继承 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("Howard", "Sales");
let john = new Person("John"); // 错误: 'Person' 的构造函数是被保护的.

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"; // 错误! name 是只读的.

参数属性

(重点) :

  • 参数属性通过给构造函数参数前面添加一个访问限定符来声明。
  • 使用 private 限定一个参数属性会声明并初始化一个私有成员;
  • 对于 publicprotected 来说也是一样。
// 对上面 Octopus 类的修改版,使用了参数属性
class Octopus {
    readonly numberOfLegs: number = 8;
    constructor(readonly name: string) { // 参数属性可以方便地让我们在一个地方定义并初始化一个成员。 
    }
}

存取器 getters/setters

通过 getters/setters 来截取对对象成员的访问,有效的控制对对象成员的访问。

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) {
    alert(employee.fullName);
}

注意:

  • 存取器要求你将编译器设置为输出 ECMAScript 5 或更高。 不支持降级到 ECMAScript 3。
  • 其次, 只带有 get 不带有 set 的存取器自动被推断为 readonly这在从代码生成 .d.ts 文件时是有帮助的,因为利用这个属性的用户会看到不允许够改变它的值。

静态属性 static

静态属性存在于类本身上面而不是类的实例上。

可分为: 静态属性静态函数

class Grid {
      // 使用 static 定义 origin,因为它是所有网格都会用到的属性。
    static origin = {x: 0, y: 0}; 
    calculateDistanceFromOrigin(point: {x: number; y: number;}) {
          // 每个实例想要访问这个属性时,都要在 origin 前面加上类名
        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}));

需注意

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

冰魂雪魄

暂无简介

文章
评论
26 人气
更多

推荐作者

櫻之舞

文章 0 评论 0

弥枳

文章 0 评论 0

m2429

文章 0 评论 0

野却迷人

文章 0 评论 0

我怀念的。

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文