【TypeScript】类、类的属性及抽象类(七)
- 【TypeScript】类、类的属性及抽象类(七)
- 一、前言
- 二、语法及属性定义
- 三、类的继承
- 四、属性类型
- 五、readonly修饰符
- 六、static 静态属性
- 七、存取器-get/set
- 八、抽象类
一、前言
传统的JavaScript程序使用基于原型的继承来创建可重用的组件,但对于熟悉使用面向对象方式的程序员来讲就有些棘手,因为他们用的是基于类的继承并且对象是由类构建出来的。
从ECMAScript 2015,也就是ECMAScript 6开始,JavaScript程序员将能够使用基于类的面向对象的方式。
使用TypeScript,允许开发者可以使用这些特性,并且编译后的JavaScript可以在所有主流浏览器和平台上运行,而不需要等到下个JavaScript版本。
二、语法及属性定义
语法:
class class_name {
// 类作用域
}
形如上述语法,通过关键字 class定义类。
Typescript类也和其他静态语言类似,其中包含 字段、构造函数、方法。
我们举一反三,利用刚才学到的知识进行定义一个学生Student 类,类中包含一个 name 属性,同时能够获取到学生的姓名 getName 方法,启动机智如你的大脑,看看该如何定义这个类:
class Student {
name: string,
constructor(name: string) {
this.name = name;
}
getName(){
return 'my name is ' + this.name;
}
}
我们声明了Student 类,这个Student类有3个成员:一个叫做 name 的属性,一个constructor构造函数和一个 getName 方法。
编译后内容会是下面这样子:
"use strict";
var Student = /** @class */ (function () {
function Student(name) {
this.name = name;
}
Student.prototype.getName = function () {
return 'my name is ' + this.name;
};
return Student;
}());
我们将这个Student 实例化,看看具体使用
const student: Student = new Student('suwu150');
console.log(student.getName())
三、类的继承
前面阶段,我们其实已经使用过类的继承和接口的继承,但只是用做例子展示,这里我们深入了解下具体的实现。
在TypeScript里,我们可以使用常用的面向对象模式。 基于类的程序设计中一种最基本的模式是允许使用继承来扩展现有的类。
语法:class ClassA extends ClassB {}
形如上述,ClassA
的定义是基于继承 ClassB
的方案实现的。
定义一个 Person类
,让 Student
继承。
class Person {
getName(){
return 'person say hi...: ';
}
}
class Student extends Person {
getStudentName(){
return 'student say hi...: my name is ';
}
}
const student: Student = new Student();
console.log(student.getName())
console.log(student.getStudentName())
Student类
是从 基类Person
中继承了 属性 和 方法。 这里 Student
是一个 派生类,它派生自 Person 基类
,通过 extends
关键字实现继承与派生。
派生类通常被称作 子类
,基类通常被称作 超类
。
由于继承关系,student实例
也就能够调用基类的 getName 方法
,也能够调用自己的 getStudentName方法
。
我们将这段代码进行TS编译,可以看看编译后的结果。
可以看到继承的实现的底层原理。
我们再来给类添加几个属性看看该如何定义,如何在方法中使用这些属性。
class Person {
name: string;
constructor(name: string) { this.name = name; }
getName(){
return 'person say hi...: ' + this.name;
}
}
class Student extends Person {
constructor(name: string) { super(name); }
getName(){
return super.getName();
}
}
class Teacher extends Person {
constructor(name: string) { super(name); }
getName(){
return super.getName();
}
}
const student: Student = new Student('Student');
const teacher: Teacher = new Teacher('Teacher');
console.log(student.getName())
console.log(teacher.getName())
这个例子中,我们使用 extends
关键字创建了 Person 的两个子类,分别是 Student 和 Teacher。
派生类包含了一个 构造函数
,它 必须调用 super(),它会执行基类的构造函数。 在构造函数里访问 this的属性之前, 一定要调用 super()。 这条规则和其他静态语言类似,是TypeScript强制执行的一条重要规则,也只有这样才能去初始化父类的一些属性。
这里我们在子类里边重写了父类的方法getName,重写父类方法方法名需要和父类保持一致。
四、属性类型
与Java静态语言类似,类里边的属性也是区分公共,私有与受保护三种类型,分别用 public、private、protected 表示。
需要注意的是,为什么我们之前没有考虑过这些类型,但我们能够随意调用?
那就是在TypeScript里,成员都默认为 public。
下面我们来看看显示定义的一些操作:
class Person {
public name: string; // name属性标识为public
private age: number; // age标识为private
protected score: string; // 不能在 Person类外使用 name,但仍然可以通过 子类的实例方法访问,因为 子类是由 Person派生而来的
public constructor(theName: string) { this.name = theName; }
public move(distanceInMeters: number) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
我们将以上类进行实例化使用
class Person {
public name: string; // name属性标识为public
private age: number; // age标识为private
protected score: string; // 不能在 Person类外使用 name,但仍然可以通过 子类的实例方法访问,因为 子类是由 Person派生而来的
public constructor(theName: string, age: number, score: string) {
this.name = theName;
this.age = age;
this.score = score;
}
public move(distanceInMeters: number) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
class Student extends Person {
move() {
super.move(200);
console.log(this.score) // 调用父级的protected
}
}
const student = new Student('suwu150', 20, '2323')
student.move();
console.log(student.name) // public可以直接访问
console.log(student.age) // private不可以直接访问,报错
console.log(student.score) // protected不可以直接访问,报错,但能够被子类Student访问
五、readonly修饰符
我们在设置类中的变量或者属性的时候,有时我们期望外界用户不能够修改,此时我们就可以使用readonly这个属性来实现。
可以使用 readonly关键字将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化。
class Person {
readonly name: string;
readonly numberOfLegs: number = 2;
constructor (theName: string) {
this.name = theName;
}
}
let person = new Person("suwu150");
person.name = "suwu150 suwu150"; // 错误! name 是只读的.
六、static 静态属性
我们也能够定义类的静态成员,可以用 static 关键字
定义静态属性,这些属性是属于类本身而非类的实例的属性。静态属性可以用来存储类级别的数据,例如单例模式中的实例对象,也可以用来提供类级别的函数和常量。
class MyClass {
static count = 0;
static incrementCount() {
this.count++;
}
}
console.log(MyClass.count); // 0
MyClass.incrementCount();
console.log(MyClass.count); // 1
在上面的例子中,我们定义了一个静态属性 count 和一个静态函数 incrementCount。我们可以直接通过类名来访问这些静态属性和函数,而不需要创建类的实例。
七、存取器-get/set
TypeScript支持通过 getters/setters
来截取对对象成员的访问。 它能帮助你有效的控制对象成员的访问。
- Getter的形式是在属性方法前增加get,用于返回对象的属性值。
- Setter的形式是在属性方法前增加set,用于设置对象的属性值。
它们可以像普通属性一样使用,只不过在具体获取值或者设置值的时候,进行监听中间处理。以下是一个示例:
class Person {
private _name: string;
get name(): string {
return this._name;
}
set name(value: string) {
this._name = value;
}
}
let person = new Person();
person.name = "John";
console.log(person.name); // 输出 "John"
在上面的示例中,我们定义了一个 Person类
,并在该类中定义了一个名为 _name 的私有属性。然后我们使用 getters
和 setters
方法来访问该属性。在类外部,我们可以像普通属性一样来设置和获取属性值。当我们设置值的时候,会调用set name(value: string)
方法,当我们获取值的时候,会调用get name(): string
方法。注意,在setter方法中,我们使用了value参数来设置属性值。
八、抽象类
抽象类可以做为其它派生类的基类使用。 它们不会直接被实例化。 不同于接口,抽象类可以包含成员的实现细节。
抽象类可以包含 抽象方法
和 非抽象方法
。
抽象方法没有具体的实现,只有方法签名,必须在子类中实现。而非抽象方法则可以有具体的实现,子类可以直接继承或者重写.
语法: abstract class ClassNameA { }
其中,ClassNameA
就是一个抽象类
abstract class Person {
abstract sayHi(): void; // 抽象方法
eat(): void { // 非抽象方法
console.log("eat food...");
}
}
上面就是定义了一个抽象类,我们在子类中进行实现。
class Student extends Person {
sayHi(): void {
console.log("hi...");
}
singsong(): void {
console.log("singsong...");
}
}
let person: Person = new Person(); // 错误:不能创建一个抽象类的实例
let student: Person = new Student(); //允许对一个抽象子类进行实例化和赋值
student.sayHi(); // 输出 "hi..."
student.eat(); // 输出 "eat food..."
student.singsong(); // 错误:"Property 'singsong' does not exist on type 'Person'."
需要注意的是,如果子类继承了一个抽象类,则必须实现其所有的抽象方法,否则子类本身也必须声明为抽象类。
以上就是TypeScript中类的一些常规用法。