理解 TypeScript 类

2,743 阅读5分钟

对象的设计图:类

在ES5中,只有对象的概念,对象是通过构造函数创建出来的,构造函数本身也是对象。所以说JavaScript是一种基于对象的语言。

ES6之后,有了class,也就是我们的类,虽然class实质上是 JavaScript 现有的基于原型的继承的语法糖。但我们同样可以借鉴C#语言的面向对象的思想去理解它。让我们够使用==基于类的面向对象的方式==。

类就是对象的设计图纸,上面记录着一个事物应该有哪些字段,哪些方法,但类不是个对象,只是对象的定义。通过new 一个类,我们才能得到一个对象,这个对象有类上描述的所有成员。

这就是基于类的面向对象的方式。程序员画好了许许多多的设计图(类)。然后new 它们,形成了一个一个的对象,对象之间彼此关联,协作。我们的程序就这样运行了。

所以我们有了类的概念,通晓类与对象的关系后,这会影响我们的编程思维方式,需要什么东西,先定义个类,在new出个对象,让其工作。我们的对象更加具象了,它有类给予它的定义。

认识类

下面我们就来认识TypeScript中的类

定义类

class Person { 
}

类中的成员

类内的成员有:字段、存取器、构造函数、方法。

字段

class Person { 
    age: number; // 这就是一个字段
}

类中的变量成员。可以被实例化的对象设置和读取。也可以被类内其他成员设置和读取。

存取器

class Person {
    private _age: number;

    get age(): number {
        return this._age;
    }

    set age(newName: number) {
        if(newName > 150){
            this._age = 150;
        }
        this._age = newAge;
    }
}

这里的get/set方法,就是_age变量的存取器。首先我们为_age变量添加一个private修饰符。表示它是个私有变量,禁止外部对它的访问。

const person = new Person();
person._age // 这句会报错,TS不允许我们读取私有变量。

要想使用_age,需要利用存取器:

const person = new Person();
person.age = 15; // 使用set age方法
const age = pereson.age; // 使用get age方法

使用存取器的好处是对变量的访问和设置,有了控制。原则上类内部的变量,外部是不能访问的,这就是封装性。要想访问,只能通过存取器方法。

构造函数

class Person {
    private _age: number;
    
    constructor(age: number) {
        this._age = age;
    }
}

constructor就是类的构造器,他的使用如下:

const person = new Person(15);

构造器就是我们在new一个类的时候,调用的方法。

方法

JavaScript中,都叫函数,但是有了类之后,类中的函数成员,就叫方法。

class Person {
    eat() { 
        ....
    }
    drink() {
        
    }
}

这里的eat和drink,就是Person类的方法。

this关键字

和原生js中捉摸不定的this不同,类中的this,非常好理解,它表示当前类的实例。

类的继承

继承,本质上讲是一种代码重用机制。 类的继承是指一个类继承另一个类,那么这个类的实例上,也会有被继承类的属性和方法。

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

class Dog extends Animal {
    bark() {
        console.log('Woof! Woof!');
    }
}

const dog = new Dog();
dog.bark();
dog.move(10);
dog.bark();

extends关键字就是指Dog类继承自Animal类。 Animal叫做父类。Dog叫做子类。

说到类的继承,同样带来了一些问题,需要我们了解一下。

  1. 父类中有的方法,子类也有,会怎么样?
  2. 子类想使用父类的构造函数,怎么办?

重写

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

class Dog extends Animal {
    move(distanceInMeters: number = 0) {
        console.log(`Dog moved ${distanceInMeters}m.`);
    }
    bark() {
        console.log('Woof! Woof!');
    }
}

const dog: Animal = new Dog();
dog.move(10); // Dog moved 10

Dog类对Animal的move类进行了重写,调用Dog实例的move方法时,会调用Dog类重写的move方法。

super关键字

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

class Dog extends Animal {
    constructor(name: string) { super(name); }
}

如果子类想使用父类中构造函数的逻辑,在子类的构造函数中,super()就是指父类构造函数。

访问修饰符

TypeScript还为我们提供了类成员的访问修饰符。public,private,protected。代表了成员的三个不同的访问级别。

  1. public:默认的访问级别,不写默认就是public。
  2. private: 只能被类内部访问,不能被类外部访问。
  3. protected: 只能被类内部,继承的类内部访问,不能被外部访问。

使用方式:

class Person {
    private name: string;
    protected age: string;
    public height: string;
}

const john = new Person("John");
john.name; // 报错
jonh.age; // 报错
jonh.height; // 正确

上面看到private和protected在类的外部,都不可以访问,public的可以。那么private和protected有什么区别呢?

class Employee extends Person {
    getInfo() {
        this.name; // 报错
        this.age; // 正确
    }
}

区别就在于在继承类中,protected的成员,仍然可以被访问到,但是private的不行。

只读属性

可以使用readonly关键字将属性设置成功只读,相当于类字段的const。

class Person {
  readonly name: string = 'john'; // 我只读
  constructor(theName: string) {
    this.name = theName; // 可以
  }

  setName() {
    this.name = 'lili'; // 报错
  }
}

只能在初始化和构造函数中赋值,其他地方不允许赋值。

静态属性

类中的成员还可以用static关键字来修饰,那么它就成了所有实例共有的成员。

class Person {
  static globalPeopleCount: number = '7000000000';
}
const john = new Person();
john.globalPeopleCount += 1;

const lili = new Person();
lili.globalPeopleCount // 7000000001

抽象类

抽象类是一种不能被实例化的类,它的目的就是用来继承的,抽象类里面可以有抽象的成员,就是自己不实现,等着子类去实现。

抽象类和抽象成员,都是用abstract修饰:

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

抽象类中还是可以有具体实现的,这样子类如果不实现,可以继承抽象类中的实现。

结束语

学习TS中的类,理解基于类的面向对象思维,可以帮助我们更好的应用面向对象思维,去解决项目的问题。