类的继承
继承的作用
- 继承可以描述类与类之间的关系
- 如果A和B都是类,并且可以描述为A是B,则A和B形成继承关系
- B是父类,A是子类
- B派生A, A继承自B
- B是A的基类,A是B的派生类
- 如果A继承自B,则A中自动拥有B中所有的成员
成员的重写
- 重写(override):子类中覆盖父类的成员
- 子类成员不能改变父类成员的类型
- 无论是属性还是方法,子类都可以对父类的相对应成员进行重写,但重写时,需要保证类型的匹配
- 需要注意this,在继承关系中,this的指向是动态的--调用方法时,根据具体的调用者确定this指向
- super关键字:在子类的方法中,可以使用super关键字读取父类成员
类型匹配
鸭子辨型法:子类的对象,始终可以赋值给父类
如果需要判断一个数据的具体子类类型,可以使用instanceof
protected 修饰符
- 受保护的成员,只能在自身和子类中访问
单根性和传递性
- 单根性: 每个类最多只能拥有一个父类
- 传递性:如果A是B的父类,并且B是C的父类,则可以认为A是C的父类
// 例子
class Tank{
x: number = 0
y: number = 0
name: string = '坦克'
shoot(){
console.log("发射子弹")
}
sayHello(){
console.log(`我是一个${this.name}`)
}
}
class PlayerTank extends Tank{
x: number = 20
y: number = 20
life: number = 5
name: string = '玩家坦克'
shoot() {
console.log("玩家发射子弹")
}
}
class EmemyTank extends Tank{
name: string = '敌方坦克'
shoot() {
console.log("敌方发射子弹")
}
test() {
super.sayHello() // 我是一个玩家坦克
}
}
const p = new PlayerTank()
p.sayHello() //我是一个玩家坦克
if(p instanceof PlayerTank){
console.log(p.life)
}
抽象类
有时,某个类只表示一个抽象概念,主要用于提取子类共有的成员,而不能直接创建它的对象,该类可以作为抽象类
给类前面加上abstract,表示该类是一个抽象类,不可以创建一个抽象类的对象
抽象成员
父类中,有些成员是必须存在的,但是不知道该成员的值或实现是什么,因此,需要有一种强约束,让继承该类的子类,必须实现该成员
抽象类中,可以有抽象成员,这些抽象成员必须在子类中实现;三种实现方式
abstract class Chess{
x: number = 0
y: number = 0
abstract readonly name: string;
abstract move(targetX: number, tergetY: number): boolean;
}
class Horse extends Chess{
readonly name: string = '马 '
move(targetX: number, tergetY: number): boolean{
this.x = targetX
this.y = targetY
console.log("马移动成功")
return true
}
}
class Pao extends Chess{
readonly name: string
constructor(){
super()
this.name = "炮"
}
move(targetX: number, tergetY: number): boolean{
this.x = targetX
this.y = targetY
console.log("炮移动成功")
return true
}
}
class Soldier extends Chess{
get name() {
return "兵"
}
move(targetX: number, tergetY: number): boolean{
this.x = targetX
this.y = targetY
console.log("兵移动成功")
return true
}
}
const h = new Horse()
const p = new Pao()
const c = new Soldier()
h.move(1,2)
p.move(2,3)
c.move(3,4)
静态成员
静态成员是指:附着在类上的成员(属于某个构造函数的成员);使用static修饰的成员,是静态成员
实例成员: 对象成员,属于某个类的对象
静态成员:非实例成员,属于某个类
静态方法中的this
实例方法中的this指向的是当前对象
而静态方法中的this指向的是当前类
class User{
constructor(
public loginId: string,
public loginPwd: string,
public name: string,
public age: number
){}
static login(loginId: string, loginPwd: string): User | undefined{
return undefined
}
}
const result = User.login("xxx", "123123 ")
索引器
对象[值],使用成员表达式
在TS中,默认情况下,不对索引器(成员表达式)做严格的类型检查
使用配置noImplicitAny: true 开启对隐式any的检查
隐式any: TS根据实际情况推导出的any类型
在索引器中,健的类型可以是字符串,也可以是数字
在类中,索引器书写的位置应该在所有成员之前
在JS中,所有的成员名本质上,都是字符串,如果使用数字作为成员名,会自动转换为字符串
作用
- 在严格的检查下,可以实现为类动态增加成员
- 可以实现动态操作类成员
const obj = {
name: 'zero',
age: 18
}
for(const key in obj) {
console.log(key, obj[key])
}
// ts类的用法
const methodName = "sayHello"
class User{
[prop: string]: any
constructor(
public name: string,
public age: number
){}
[methodName] (){}
}
const u = new User("zero", 18)
u[methodName]()
// 不在构造函数的声明里
console.log(u["sex"])
class MyArray{
[index: number]: string
0: "1"
}
this指向约束
JS中this的几种情况
- 如果直接调用函数(全局调用),this指向全局对象或undefined(启用严格模式)
- 如果使用
对象.方法调用,this指向对象本身 - 如果是dom事件的处理函数,this指向事件处理对象
特殊情况
- 箭头韩式,this在函数声明时确定指向,指向函数位置的this
- 使用bind, apply, call手动绑定this对象
TS中的this
- 配置
noImplicitThis: true,表示不允许this隐式的指向any - 允许在书写函数时,手动声明该函数中的this的指向,将this作为函数的第一个参数,该参数只用于约束this,并不是真正的参数,也不会出现在编译结果中
interface IUser{
name: string
age: number
sayHello(this: IUser): void
}
const u: IUser = {
name: 'zero',
age: 18,
sayHello() {
console.log(this.name, this.age)
}
}
const say = u.sayHello