# 类
与ES6一样,Typescript也是用class
来定义类,不同于ES5以前用函数和原型来实现继承
# 举个🌰
class Person {
name: string
age: number
sex: string
// 构造函数
constructor(name: string = '名字', age: number = 0, sex: string = '未知') {
this.name = name
this.age = age
this.sex = sex
}
getName(){
console.log(`早晨,我系${this.name}`)
}
getAge() {
console.log(`我今年${this.age}岁`)
}
getJob(job?: string) {
console.log(`我的职业是${job || '自由职业'}`)
}
}
// 创建实例
const linna = new Person('Linna', 30, 'female')
// 调用实例的方法
linna.getName() // 早晨,我系Linna
console.log(linna)
# 继承
通过继承扩展类,接👆🌰,子/派生类从父/基类继承了属性和方法,👇🌰中的子类Coder
通过关键字extends
继承了父类Person
的属性和方法,所以可以调用父类的getName
等方法
class Coder extends Person {
code() {
console.log('敲了几十万行代码')
}
}
const coder = new Coder('程序猿')
coder.getName() // 调用父类的方法
coder.code() // 敲了几十万行代码
👇来个复杂点的🌰,这里创建了两个子类,里面都有一个构造函数constructor
,子类中的constructor
必须调用super
,即父类的构造函数
在构造函数里访问this
的属性之前,我们 一定要调用super
。 这个是TypeScript强制执行的一条重要规则。
class Writer extends Person {
// 调用父类构造方法
constructor(name: string = '莫言') {
// 派生/子类的构造函数必须包含 "super" 调用
//this.name = name // 报错: 访问派生类的构造函数中的 "this" 前,必须调用 "super"
super(name)
}
// 重写方法
getJob() {
console.log(`我的职业是作家`)
}
write(num: number = 1000) {
console.log(`今天写了${num}字`)
}
}
class Sportsman extends Person {
constructor(name: string = '詹姆斯') {
super(name)
}
getJob() {
console.log(`我的职业是运动员`)
}
playBasketball(num: number = 200) {
console.log(`今天投了${num}个三分`)
}
}
const writer = new Writer('老舍')
writer.getJob() // 我的职业是作家
writer.write() // 今天写了1000字
const sportsman = new Sportsman('奥尼尔')
sportsman.getJob()
sportsman.playBasketball()
这里不仅继承了父类的方法,而且还重写了父类的方法,使父类的方法根据不同的类有不同的功能,不仅如此还也可拥有自身的方法。
# 多态
// 父类型引用指向子类型实例
const curry: Person = new Sportsman('库里')
// 虽然声明为父类型,但是仍会调用子类重写的方法
curry.getJob() // 我的职业是运动员
// 当子类型有扩展方法时,不能让子类型引用指向父类型的实例
// 报错:类型 "Person" 中缺少属性 "playBasketball",但类型 "Sportsman" 中需要该属性。
const yao: Sportsman= new Person('姚明')
// 不会报错
const luxun: Writer = new Person('鲁迅')
luxun.getName()
# 修饰符
# public
在TypeScript里,因为属性都默认为public
,表示公共,所以可以在声明它的类的外部自由访问属性。
# private
当属性被设置为private
时,表示私有的,类的外部无法访问属性。
# protected
当属性被设置为protected
时,表示受保护的,除了子类,类的外部无法访问属性。
# readonly
当属性被设置为readonly
时,表示只读的,只读属性必须在声明时或构造函数内初始化。
改造下👆用到的🌰
// public默认为隐式,一般不需要显示的表示
class Person {
public name: string
public constructor(name: string) {
this.name = name
}
public eat(food: string = '米饭') {
console.log(`今日食左${food}`)
}
}
class Child extends Person {
private cry: boolean = true
protected sex: string = 'boy'
readonly mother: string = 'Mom'
eat(food: string = '奶粉') {
super.eat(food)
}
}
const child = new Child('ryan')
// console.log(child.name) // 公共实例可见
// console.log(child.cry) // 私有的实例🙅♂️见 报错: 属性“cry”为私有属性,只能在类“Child”中访问。
// console.log(child.sex) // 受保护的实例🙅♂️见 报错: 属性“sex”受保护,只能在类“Child”及其子类中访问。
// console.log(child.mother = 'Mum') // 只读属性实例🙅♂️重新赋值 报错: 无法分配到 "mother" ,因为它是只读属性。
class Baby extends Child {
eat(food: string = '奶') {
// console.log(this.cry) // 子类🙅访问父类私有属性 报错: 属性“cry”为私有属性,只能在类“child”中访问。
// console.log(this.sex) // 子类可以访问父类受保护属性
// console.log(this.mother = 'Mother') // 只读属性子类🙅重新赋值 报错: 无法分配到 "mother" ,因为它是只读属性。
super.eat(food)
}
}
# 总结
修饰符 | 子类 | 实例 |
---|---|---|
public | 可见 | 可见 |
protected | 可见 | 哒咩 |
private | 哒咩 | 哒咩 |
readonly | 可见🙅改 | 可见🙅♂️改 |
# getters/setters
Typescript可以使用getters/setters
来拦截对象属性的访问。
class Car {
brand: string = '品牌'
series: string = '系列'
get carInfo () {
console.log('获取车辆信息')
return this.brand + '_' + this.series
}
set carInfo (val: string) {
console.log('设置车辆信息', val)
let vals = val.split('_')
this.brand = vals[0]
this.series = vals[1]
}
}
let car = new Car()
car.brand = '宝马'
car.series = '3系'
console.log(car.carInfo) // 获取车辆信息 宝马_3系
console.log(car.carInfo = '天籁_公爵', car.carInfo)
// 设置车辆信息
// 获取车辆信息
// 天籁_公爵 天籁_公爵
# static
当属性被设置为static
时,表示该属性为类本身的属性,而非实例的属性
class City {
name: string = '深圳'
static name1: string = '台湾'
}
let city = new City()
console.log(city.name)
// console.log(city.name1) // 实例🙅访问静态属性/方法 属性“name1”在类型“City”上不存在。你的意思是改为访问静态成员“City.name1”吗?
console.log(City.name1) // 台湾
# abstract
abstract
关键字指的是抽象类,用于定义抽象类和抽象类内部的抽象方法。
抽象类作为其他子类的父类,但是它不能实例化,为了让子类实例化及实现内部抽象方法。
抽象类不同于接口的约束作用,它可以包含成员的实现细节。
// 定义抽象类
abstract class Animal {
// 定义抽象类的抽象方法,无具体实现
abstract sound (): string
// 重写run方法
run() {
console.log('run! deng~deng~deng')
}
}
// console.log(new Animal()) // 报错: 无法创建抽象类的实例
class Dog extends Animal {
sound(): string {
console.log('叫ing...')
return 'woof~~~'
}
run() {
return '四条腿跑'
}
}
const puppy = new Dog()
console.log(puppy.sound()) // 叫ing... woof~~~
console.log(puppy.run()) // 四条腿跑
# 参数属性
通过设置参数属性,Typescript隐式帮我们创建和初始化类对应的属性,不需要频繁的通过this.属性名
这种方式创建和初始化属性,请看👇🌰
class Person2 {
constructor (public name: string) {}
// 等同于
// name: string
// constructor (name: string) {
// this.name = name
// }
// 以下修饰符同理
// constructor (private name: string) {}
// constructor (protected name: string) {}
// constructor (readonly name: string) {}
}
let person2 = new Person2('Luna')
// 当参数属性为private时,报错:属性“name”为私有属性,只能在类“Person2”中访问。
// 当参数属性为protected时,报错:属性“name”受保护,只能在类“Person2”及其子类中访问。
console.log(person2.name)
// 当参数属性为readonly时,并重新赋值时,报错:无法分配到 "name" ,因为它是只读属性;不重新赋值不报错
// console.log(person2.name = 'James')