1. 类
1.1 构造器
- 使用类的例子
class Dog {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
// 引用任何一个类成员的时候都用了this.它表示我们访问的是类的成员
say() {
return console.log(`${this.name}今年${this.age}岁`);
}
}
const erha = new Dog("erha", 4);
erha.say();
- 分析
- 我们声明了一个Dog类,这个类有4个成员:两个属性(name,age),一个构造函数和一个say方法
- 使用new构造了Dog类的一个实例。它会调用之前定义的构造函数,创建一个Dog类型的新对象,并执行构造函数初始化它。
- 在引用类的成员的时候都用了this。它表示我们访问的是类的成员。
- 在实例方法中,this就表示当前的实例。
1.2 继承
- 在TypeScript里,使用继承来扩展现有的类。
- 继承案例代码(一)
// 在TypeScript里,允许使用继承来扩展现有的类
class Animal {
move(distanceInMeters: number = 0) {
console.log(`Animal move ${distanceInMeters}m`);
}
}
class Cat extends Animal {
bark() {
console.log("Woof Woof");
}
}
const cat = new Cat();
cat.bark();
cat.move();
cat.move(20);
- 输出结果
Woof Woof
Animal move 0m
Animal move 20m
- 分析
- 类从基类中继承了属性和方法。Cat是一个派生类,它派生自Animal基类,通过extends关键字。
- 派生类通常被称为子类,基类通常被称为超类
- 因为Cat继承了Animal的功能,因此我们可以创建一个Cat的实例,它能够bark()和move()
- 继承案例代码(二)
class Animals {
name: string;
constructor(theName: string) {
this.name = theName;
}
move(distanceInMeters: number = 0) {
console.log(`${this.name} move ${distanceInMeters}`);
}
}
// 继承Animals类
class Snake extends Animals {
constructor(name: string) {
super(name);
}
move(distanceINmeters = 5) {
console.log("Slithering...");
super.move(distanceINmeters);
}
}
// 继承Animals类
class Horse extends Animals {
constructor(name: string) {
super(name);
}
move(distanceINmeters = 45) {
console.log("Galloping...");
super.move(distanceINmeters);
}
}
let sam = new Snake("Sammy the Python");
let tom = new Horse("Tommy the Palomino");
sam.move();
tom.move();
- 代码执行结果
Slithering...
Sammy the Python move 5
Galloping...
Tommy the Palomino move 45
- 分析
- 使用extends关键字创建了Animals的两个子类: Snake和Horse
- 两个派生类包含了一个构造函数,它必须调用super(),它会执行基类的构造函数。
- 在构造函数里访问
this的属性之前,我们一定要调用super()
1.3 修饰符
- public
- 在TypeScript里,成员都默认为
public。也可以明确的将一个成员 标记成public.
- 在TypeScript里,成员都默认为
- private
- 当成员被标记成private时,它就不能在声明它的类的外部访问。
- 只有当另外一个类型中也存在这样一个
private成员, 并且它们都是来自同一处声明时,我们才认为这两个类型是兼容的
- protected
- protected修饰符与private修饰符的行为很相似,但有一点不同,protected成员在派生类中仍然可以访问。
- 构造函数也可以被标记成protected。这意味着这个类不能在包含它的类外被实例化,但是能被继承。
- 使用protected案例代码
class Person2 {
protected name: string;
constructor(name: string) {
this.name = name;
}
}
class Employee2 extends Person2 {
private department: string;
constructor(name: string, department: string) {
super(name);
this.department = department;
}
public getElveatorPitch() {
return `name is ${this.name},department is ${this.department}`;
}
}
let Bob = new Employee2("Bob", "sales");
Bob.getElveatorPitch();
console.log(Bob.name); // 错误
2. 代码分析
Person2类有protected属性name, 派生类Employee2实例方法可以访问到Person2类的name属性,
但是不能在Person2类外部使用name属性
4. readonly修饰符
1. 使用readonly关键字将属性设置为只读的。只读属性必须在声明时或构造函数里被初始化。
5. 静态属性 static\
1.4 抽象类 abstract
1. 抽象类作为其他派生类的基类使用。它们一般不会直接被实例化。`abstract`关键字是用于定义抽象类和在抽象类内部定义抽象方法。
2. 抽象类中的抽象方法不包含具体实现,并且必须在派生类中实现。
3. 抽象方法必须包含`abstract`关键字。
- 案例代码
abstract class Animal3 {
abstract makeSound(): void;
move(): void {
console.log("rooming the earch...");
}
}
abstract class Department {
constructor(public name: string) {}
printName(): void {
console.log("Department name:" + this.name);
}
// 抽象类中抽象方法不包含具体实现并且必须在派生类中实现
abstract printMeeting(): void;
}
class AccountingDepartment extends Department {
// 在派生类的构造函数中调用super()
constructor() {
super("Accounting and Auditing...");
}
printMeeting(): void {
console.log(" The Accounting Department meets each Monday at 10am..");
}
generateReports(): void {
console.log("generating accounting reports...");
}
}
// 创建一个抽象类类型的引用
let department: Department;
department = new Department(); // 会出错,不能创建一个抽象类的实例
department = new AccountingDepartment();
department.printName();
department.printMeeting();
department.generateReports(); // 方法在声明抽象类中不存在
- 代码分析
-
- 声明了两个抽象类Animal3和Department,
-
- Department抽象类中有,构造器、printName方法、抽象方法printMeeting(抽象类中的定义抽象方法只定义方法签名,不包含方法体,但是,在派生类中必须要实现抽象类定义的抽象方法)
-
- AccountingDepartment类实现了抽象类Department,在构造函数中调用Super(),定义自身的方法generateReports,实现继承抽象类中的抽象方法。
-
- 不能直接创建一个抽象类的实例对象
-
- 允许对一个抽象子类进行实例化和赋值
2. 接口
2.1 使用接口
- TypeScript的核心原则之一是对值所具有的结构进行检查。
- 接口用来定义一个类的结构,用来定义一个类中应该包含那些属性和方法
- 可以重复定义接口
- 接口中的所有的属性都不能有实际值
- 接口只定义对象的结构,而不考虑实际值
- 接口中的方法,只是定义方法签名但不包含方法体
- 案例代码
function printLabel(labelObject: { label: string }) {
console.log(labelObject.label);
}
let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);
/*********************************/
type myType = {
name: string;
age: number;
};
let xm: myType = {
name: "xiaoming",
age: 5,
};
/********************************************* */
interface myInterface {
name: string;
age: number;
}
interface myInterface {
gender: string;
}
let objXh: myInterface = {
name: "xiaohong",
age: 6,
gender: "female",
};
- 代码分析
- 类型检查器会查看printLabel的调用。其中有一个对象参数labelObject
- 要求这个对象参数有一个label属性,值为string类型
- 接口代码
interface labelValue {
label: string
}
function printLabel(labelObject: labelValue){
console.log(labelObject.label)
}
let myobj2 = { size: 11, label: "Size 11 Object" }
printLabel(myObj2)
2.2 可选属性
- 接口里的属性不全都是必须的。
2.3 实现接口
- 定义类时可以使用implements去实现一个接口。(实现接口就是使类满足接口的要求)
- 案例代码
interface ClockInterface {
name: string;
setTime(): void;
}
class Clock implements ClockInterface {
name: string;
constructor(name: string) {
this.name = name;
}
setTime() {}
}
- 接口与抽象类的区别
- 抽象类中,可以有抽象方法也可以有普通方法。接口中都是抽象方法
- 在使用抽象类的时候使用的是extends实现,接口使用的是implements实现
- 接口定义一个标准,限制类,符合接口的标准
3. 属性的封装
- 通过对属性的控制,放置实例对象直接更改属性值
- 案例代码
class Heros {
private _name: string;
private _age: number;
constructor(name: string, age: number) {
this._name = name;
this._age = age;
}
getName() {
return this._name;
}
setName(value: string) {
this._name = value;
}
setAge(value: number) {
if (value >= 0) this._age = value;
}
}
const zs = new Heros("zs", 18);
console.log(zs);
zs.setName("lisi");
console.log(zs.getName());
zs.setAge(10);
console.log(zs);
-
分析
- 通过设置get和set,控制实例对象直接更改属性值
-
TS中设置get/set方法
class Heros {
private _name: string;
private _age: number;
constructor(name: string, age: number) {
this._name = name;
this._age = age;
}
// TS中提供的设置修改属性的get/set方法
get name() {
console.log("get name()执行了");
return this._name;
}
set name(value: string) {
this._name = value;
}
get age() {
return this._age;
}
set age(value: number) {
if (value >= 0) {
this._age = value;
} else {
console.log("更改的年龄不合法");
}
}
setAge(value: number) {
if (value >= 0) this._age = value;
}
}
// 实例化对象
const zs = new Heros("zs", 18);
zs.age = 33;
zs.name = "悟空";
console.log(zs);
4. 泛型
- 可以使用泛型来创建可重用的组件
4.1 使用泛型的例子
- 普通函数案例代码
// 不使用泛型的函数
function fn(arg: number): number {
return arg
}
// 使用any类型来定义函数
function identity(arg: any): any{
return arg
}
// 使用类型变量
function ident<T>(arg: T): T {
return arg
}
-
代码分析
- 使用any类型当作函数的返回值和参数类型,传入的值和返回值类型是相同的。
- 不管传入任何类型的值都会被返回
- 使用类型变量,表示类型而不是值,使返回值的类型与传入参数的类型是相同的。
-
使用泛型的两种方式
// 第一种
const result1 = ident<string>("hello...");
// 第二种
const result2 = ident("world...");
console.log(result1);
console.log(result2);
- 分析
- 第一种使用泛型的方式:传入了所有的参数,包括参数类型
- 明确的指定了
T是string类型 - 第二种使用泛型的方式: 利用了类型推论,即编译器会根据传入的参数自动地帮助我们确认
T的类型