TypeScript 类的基础:从定义到实例化,让你快速掌握(一)https://developer.aliyun.com/article/1426388
类中的构造函数
在 TypeScript 中,类可以使用构造函数来创建和初始化类的实例对象。构造函数是类的特殊成员,用于在创建类的实例时初始化其变量和状态,并执行任何其他必要操作。
在 TypeScript 中,可以通过 constructor
关键字来定义构造函数,它是一个特殊的成员函数,没有返回值。参数列表中声明的参数将作为构造函数的输入参数。
以下是一个简单的 TypeScript 类构造函数的示例:
class Person { public name: string; public age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } public sayHello() { console.log(`Hello, my name is ${this.name}, and I'm ${this.age} years old.`); } } const person1 = new Person('Tom', 30); person1.sayHello(); // 输出 "Hello, my name is Tom, and I'm 30 years old."
在上面的示例中,Person
类使用了构造函数来初始化类的 name
和 age
成员变量。构造函数使用了 name
和 age
参数来初始化这些变量。这样,在使用 new
运算符创建新的 Person
实例时,构造函数将会被自动调用,并传递相应的参数。从而使得 person1
实例的 name
和 age
成员变量都被赋值为 'Tom'
和 30
。
需要注意的是,构造函数在类的实例化时只被调用一次,因此用于初始化类的属性、方法以及绑定事件等。如果需要在该类的实例上添加额外的方法或属性,可以通过原型链来实现:
class Person { public name: string; public age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } public sayHello() { console.log(`Hello, my name is ${this.name}, and I'm ${this.age} years old.`); } } Person.prototype.sayGoodbye = function() { // 在 Person 类的原型上添加方法 console.log(`Goodbye, my name is ${this.name}, and I'm leaving.`); } const person1 = new Person('Tom', 30); person1.sayHello(); // 输出 "Hello, my name is Tom, and I'm 30 years old." person1.sayGoodbye(); // 输出 "Goodbye, my name is Tom, and I'm leaving."
在上面的示例中,通过 Person.prototype
添加了一个新的方法 sayGoodbye()
,它可以在 person1
实例上调用。注意,该方法没有在类定义中声明,是通过原型链模型进行继承和扩展的。
总之,在 TypeScript 类中,构造函数是一个特殊的成员函数,用于在创建类的实例时初始化其变量和状态。在构造函数中要注意赋值对象的属性,如果需要在该类的实例上添加额外的方法或属性,可以通过原型链来实现。
III. TypeScript 类的继承
继承的概念和语法
在 TypeScript 中,类可以通过继承从其他类中派生出子类,并获得其成员变量和成员函数。继承是一种面向对象编程的重要特性,它可以增强代码的可重用性和可扩展性,并简化代码逻辑。
在 TypeScript 中,可以使用 extends
关键字来实现继承,子类会自动获得父类的成员变量和成员函数,同时可以在子类中添加其他的成员变量和成员函数,也可以重写父类的成员函数。继承关系可以通过 super
来访问父类的成员变量和成员函数。以下是一些示例:
class Person { public name: string; protected age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } public sayHello() { console.log(`Hello, my name is ${this.name}, and I'm ${this.age} years old.`); } } class Student extends Person { public major: string; constructor(name: string, age: number, major: string) { super(name, age); // 调用父类的构造函数 this.major = major; } public sayHello() { // 子类中重写父类的sayHello方法 console.log(`Hello, my name is ${this.name}, and I'm ${this.age} years old. My major is ${this.major}.`) } public study() { console.log(`${this.name} is studying.`); } } const student1 = new Student('Tom', 20, 'Computer Science'); student1.sayHello(); // 输出 "Hello, my name is Tom, and I'm 20 years old. My major is Computer Science." student1.study(); // 输出 "Tom is studying." // console.log(student1.age); // 报错:Property 'age' is protected and only accessible within class 'Person' and its subclasses.
在上面的示例中,Student
类继承自 Person
类,并在构造函数中调用了父类 Person
的构造函数 super(name, age)
,完成了对父类成员变量的初始化。子类 Student
中添加了一个 major
成员变量和 study()
方法,并重写了父类 Person
的 sayHello()
方法,添加了 major
成员变量的输出。父类 Person
中的 age
成员变量使用了 protected
访问修饰符,子类 Student
可以继承使用但无法在类外部直接访问。
总之,在 TypeScript 中,类可以通过继承从其他类中派生出子类,并获得其成员变量和成员函数。在子类中可以重写父类的成员函数和添加其他的成员变量和成员函数。继承关系可以通过 super
来访问父类的成员变量和成员函数。
子类的构造函数
在 TypeScript 中,子类的构造函数必须调用父类的构造函数,以初始化从父类继承来的成员变量。否则会报错。此时,可以使用 super()
来调用父类构造函数,并传递相应的参数。子类构造函数也可以添加自己的成员变量和成员函数。以下是一些示例:
class Person { public name: string; protected age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } public sayHello() { console.log(`Hello, my name is ${this.name}, and I'm ${this.age} years old.`); } } class Student extends Person { public major: string; constructor(name: string, age: number, major: string) { super(name, age); // 调用父类的构造函数 this.major = major; } public sayHello() { // 子类中重写父类的sayHello方法 console.log(`Hello, my name is ${this.name}, and I'm ${this.age} years old. My major is ${this.major}.`) } public study() { console.log(`${this.name} is studying.`); } } const student1 = new Student('Tom', 20, 'Computer Science'); student1.sayHello(); // 输出 "Hello, my name is Tom, and I'm 20 years old. My major is Computer Science." student1.study(); // 输出 "Tom is studying."
在上面的示例中,子类 Student
的构造函数接收了三个参数 name
、age
和 major
,并使用 super(name, age)
调用父类 Person
的构造函数来初始化成员变量 name
和 age
。构造函数中的 this.major = major;
表示初始化子类的 major
成员变量,可以在子类中访问 major
。然后子类 Student
可以自己定义 study()
方法。
总之,在 TypeScript 中,子类的构造函数必须调用父类的构造函数,以初始化从父类继承来的成员变量。使用 super()
来调用父类构造函数,并传递相应的参数。子类构造函数也可以添加自己的成员变量和成员函数。
子类中的成员变量和成员函数
在 TypeScript 中,子类可以继承父类的成员变量和成员函数,并且可以添加自己的成员变量和成员函数。子类还可以重写父类的成员函数,以实现自己的行为。以下是一些示例:
class Person { public name: string; protected age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } public sayHello() { console.log(`Hello, my name is ${this.name}, and I'm ${this.age} years old.`); } } class Student extends Person { public major: string; protected grade: number; constructor(name: string, age: number, major: string, grade: number) { super(name, age); // 调用父类的构造函数 this.major = major; this.grade = grade; } public sayHello() { // 子类中重写父类的sayHello方法 console.log(`Hello, my name is ${this.name}, and I'm ${this.age} years old. My major is ${this.major}.`) } public study() { console.log(`${this.name} is studying.`); } public getGrade() { // 子类中新增一个getGrade方法 return this.grade; } } const student1 = new Student('Tom', 20, 'Computer Science', 90); student1.sayHello(); // 输出 "Hello, my name is Tom, and I'm 20 years old. My major is Computer Science." student1.study(); // 输出 "Tom is studying." console.log(student1.getGrade()); // 输出 "90"
在上述示例中,子类 Student
继承了父类 Person
的属性 name
和 age
,并添加了自己的属性 major
和 grade
。在构造函数中,子类使用 super(name, age)
调用父类的构造函数来设置 name
和 age
属性。子类还重写了父类的 sayHello()
方法,添加了自己的输出内容。子类 Student
中定义了自己的 study()
方法和 getGrade()
方法来获取自己的成绩。
总之,在 TypeScript 中,子类可以继承父类的成员变量和成员函数,并且可以添加自己的成员变量和成员函数。子类还可以重写父类的成员函数,以实现自己的行为。如果子类继承的成员变量或成员函数与父类重名,则子类的成员会覆盖父类的相应成员。
重写父类的方法
在 TypeScript 中,子类可以重写(override)父类的方法,以实现自己的行为。重写父类方法的过程是:子类定义与父类相同的方法名和参数列表,然后在子类中重新实现父类方法的行为。覆盖父类的成员函数可以使用 super
关键字来调用父类的成员函数。以下是一个简单的示例:
class Person { public name: string; protected age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } public sayHello() { console.log(`Hello, my name is ${this.name}, and I'm ${this.age} years old.`); } public sayGoodbye() { console.log(`Goodbye, my name is ${this.name}, and I'm leaving.`); } } class Student extends Person { public major: string; constructor(name: string, age: number, major: string) { super(name, age); this.major = major; } public sayHello() { // 子类中重写父类的sayHello方法 console.log(`Hello, my name is ${this.name}, and I'm ${this.age} years old. My major is ${this.major}.`) } public sayGoodbye() { // 子类中重写父类的sayGoodbye方法 super.sayGoodbye(); // 调用父类的sayGoodbye方法 console.log(`${this.name} is also leaving.`); } } const student1 = new Student('Tom', 20, 'Computer Science'); student1.sayHello(); // 输出 "Hello, my name is Tom, and I'm 20 years old. My major is Computer Science." student1.sayGoodbye(); // 输出 "Goodbye, my name is Tom, and I'm leaving." 和 "Tom is also leaving."
在上述示例中,子类 Student
重写了父类 Person
的 sayHello()
方法和 sayGoodbye()
方法,以实现自己的行为。当子类中的 sayHello()
方法被调用时,会输出自己的信息 "Hello, my name is Tom, and I'm 20 years old. My major is Computer Science."
。当子类中的 sayGoodbye()
方法被调用时,它先使用 super.sayGoodbye()
调用父类的 sayGoodbye()
方法,然后输出自己的信息 "Tom is also leaving."
。这样子类 Student
就在父类的基础上扩展了自己的功能。
总之,在 TypeScript
中,子类可以重写父类的方法,以实现自己的行为。重写父类方法的过程是:子类定义与父类相同的方法名和参数列表,然后在子类中重新实现父类方法的行为。覆盖父类的成员函数可以使用 super
关键字来调用父类的成员函数。
抽象类和抽象方法
在 TypeScript
中,抽象类是一种不能被实例化的类,它的主要作用在于定义一个接口或者基类来供其它类继承或者实现,并梳理出其中的方法和属性,要求子类必须实现或重写某些方法和属性。抽象类中可以包含抽象方法和实现方法,其中抽象方法是没有具体实现的,需要在子类中进行具体的实现。
定义抽象类需要使用 abstract
关键字并且至少包含一个抽象方法,抽象方法使用 abstract
关键字标记,但是不能有具体的实现,需要在子类中进行实现。以下是一个简单的示例:
abstract class Animal { public name: string; constructor(name: string) { this.name = name; } abstract makeSound(): void; // 定义一个抽象方法 public move(distance: number = 0) { // 定义一个实现方法 console.log(`${this.name} moved ${distance}m.`); } } class Dog extends Animal { constructor(name: string) { super(name); } public makeSound() { // 子类中实现父类的抽象方法 console.log(`${this.name} barks.`); } } const dog1 = new Dog('Tom'); dog1.makeSound(); // 输出 "Tom barks." dog1.move(10); // 输出 "Tom moved 10m."
在上述示例中,Animal
类是一个抽象类,其构造函数接收一个 name
参数,并且包含一个抽象方法 makeSound()
和一个实现方法 move()
。该抽象类不能直接实例化,而必须被继承。子类 Dog
继承自 Animal
并实现了 makeSound()
方法,同时使用 super(name)
调用父类的构造函数。
总之,在 TypeScript
中,抽象类是一种不能被实例化的类,它的主要作用在于定义一个接口或者基类来供其它类继承或者实现,并梳理出其中的方法和属性,要求子类必须实现或重写某些方法和属性。抽象类中可以包含抽象方法和实现方法,其中抽象方法是没有具体实现的,需要在子类中进行具体的实现。定义抽象类使用 abstract
关键字,并且至少包含一个抽象方法,抽象方法使用 abstract
关键字标记,但是不能有具体的实现,需要在子类中进行实现。
接口继承
在 TypeScript 中,接口可以继承自另一个接口,这样就可以从基类接口中继承属性和方法,并且在衍生接口中扩展自己的属性和方法。接口继承的语法使用 extends
关键字,格式如下:
interface 基类接口 { // 属性和方法 } interface 衍生接口 extends 基类接口 { // 属性和方法 }
以下是一个示例:
interface Animal { name: string; eat(food: string): void; } interface Dog extends Animal { breed: string; makeSound(): void; } class Labrador implements Dog { public name: string; public breed: string; constructor(name: string) { this.name = name; this.breed = 'Labrador'; } public eat(food: string) { console.log(`${this.name} eats ${food}.`); } public makeSound() { console.log(`${this.name} barks.`); } } const dog1 = new Labrador('Tom'); dog1.eat('bone'); // 输出 "Tom eats bone." dog1.makeSound(); // 输出 "Tom barks."
在上述示例中,定义了一个 Animal
接口和一个 Dog
接口,接口 Dog
继承自接口 Animal
并添加了一个 breed
属性和一个 makeSound()
方法。子类 Labrador
实现了接口 Dog
的所有属性和方法,并添加了一个构造函数和一个 eat()
方法,用于实现自己的行为。最后,通过实例化 Labrador
类的对象来使用这个类。
总之,在 TypeScript
中,接口可以继承自另一个接口,这样就可以从基类接口中继承属性和方法,并且在衍生接口中扩展自己的属性和方法。接口继承的语法使用 extends
关键字,格式如上述示例所示。使用接口继承可以使接口之间产生关联,减少重复代码的出现,并且简化了类的实现。
IV. TypeScript 类的泛型
泛型类的定义与实例化
在 TypeScript
中,泛型类是一种可以用来创建多个不同类型的类,其具体类型在使用时才被指定,这使得类能够适应多种数据类型并减少代码重复。泛型类有一个类型参数列表,其中用尖括号(<
和 >
)包括一个或多个类型参数,并在类中使用这些类型参数来定义属性和方法。
以下是一个简单的泛型类示例:
class DataStorage<T> { private data: T[] = []; public add(item: T) { this.data.push(item); } public remove(item: T) { const index = this.data.indexOf(item); if (index >= 0) { this.data.splice(index, 1); } } public getAll() { return this.data; } } const stringStorage = new DataStorage<string>(); // 创建一个存储字符串的泛型类实例 stringStorage.add('apple'); stringStorage.add('banana'); console.log(stringStorage.getAll()); // 输出 ["apple", "banana"] stringStorage.remove('apple'); console.log(stringStorage.getAll()); // 输出 ["banana"] const numberStorage = new DataStorage<number>(); // 创建一个存储数字的泛型类实例 numberStorage.add(1); numberStorage.add(2); console.log(numberStorage.getAll()); // 输出 [1, 2] numberStorage.remove(1); console.log(numberStorage.getAll()); // 输出 [2]
在上述示例中,定义了一个泛型类 DataStorage
,其类型参数 T
确定了类中定义的属性和方法的类型。在泛型类实例化时,需要指定其中的类型参数,具体类型则被推断出。创建了两个泛型类实例,一个存储字符串,另一个存储数字,并添加、删除和获取其中的元素。
总之,在 TypeScript
中,泛型类是一种可以用来创建多个不同类型的类,其具体类型在使用时才被指定。泛型类有一个类型参数列表,其中用尖括号(<
和 >
)包括一个或多个类型参数,并在类中使用这些类型参数来定义属性和方法。泛型类的定义和普通类类似,但需要在类名后面加上尖括号和一些类型参数。在使用时,需要指定具体类型参数。
TypeScript 类的基础:从定义到实例化,让你快速掌握(三)https://developer.aliyun.com/article/1426390