TypeScript面向对象
类的定义
- 与JS不同的是,成员属性需要在前面进行提前声明
class Person{//需要在前面对成员变量进行声明name: string//声明的时候,可以对值进行初始化,初始化可以带有类型注解,也可以省略age = 18//constructor中的变量依旧要进行类型注解constructor(name:string,age:number) {this.name = name,this.age = age}
}const p1 = new Person("zhangcheng", 20)console.log(p1)
类的继承
- 与JS中的继承是一样的
- 使用 extends进行继承
- 可以使用super
类的成员修饰符
在TypeScript中,类的属性和方法支持三种修饰符:public、private、protected
- public:修饰的是在任何地方可见,公有的属性或方法,默认编写的属性就是public
- private:修饰的仅在同一类中可见,私有的属性或者方法
- protected:修饰的是仅在类自身以及子类中可见,受保护的属性或者方法
class Person{//可以自由访问,默认就是publicpublic name: string//仅能在Person类中进行访问private age = 18//仅能在Person中,以及Person的子类中进行访问protected address:string = "河北省"constructor(name:string,age:number,address:string) {this.name = name,this.age = age,this.address = address}
}
只读属性readonly
- 正常情况下,类创建的实例是可读可写的
- 我们可以定义一个成员属性为只读属性
class Person{//这样name就只能读取,不能修改了readonly name: stringconstructor(name:string) {this.name = name,}
}
getters/setters
- 主要是对私有属性,进行设置
- 其目的是为了拦截,防止一些非法的操作发生
class Person{private _age: numberconstructor(_age: number) {this._age = _age}//对值进行设置set age(newValue: number) {//若设置的年龄过大或者过小则直接忽略if (newValue > 0 && newValue < 150) {this._age = newValue}}get age(): number{return this._age}
}const p1 = new Person(18)
console.log(p1.age)
p1.age = 1000
console.log(p1.age)
参数属性
- 前面我们知道在定义一个类中的成员属性,需要在前面进行声明
- 而TS提供了一种语法糖,可以使代码看起来比较简洁
- 在 constructor中的变量前面,用写明修饰符public、private、protected、readonly即可
class Person{private _age: numberconstructor(_age: number) {this._age = _age}
}//相当于以下写法
class Person{constructor(private _age: number) {this._age = _age}
}
抽象类abstract
- 抽象类的特点
- 抽象类不能通过new创建实例
- 抽象类可以包含抽象方法,也可以包含实现的具体方法
- 有抽象类的方法,必须是一个抽象类
- 抽象方法必须在子类中实现
- 现在有这样一个需求
- 定义一个 shape抽象类,里面有一个getArea的抽象方法
- 分别定义 三角形,圆形,长方形等多种形状的类
abstract class Shape {//抽象方法,必须存在于抽象类中,抽象类不必具体实现abstract getArea():number
}//圆形
class Circle extends Shape{//继承自抽象类的子类,要具体实现其抽象方法getArea(): number {return 3.14*3*3}
}//长方形
class Juxing extends Shape{getArea(): number {return 3*4}
}function getShapeArea(shape: Shape) {//只需要调用Shape中的方法即可求出面积console.log(shape.getArea());
}const c1 = new Circle()
const j1 = new Juxing()
console.log(getShapeArea(c1))
console.log(getShapeArea(j1))
类的特性
- 可以通过new 创建实例对象
- 可以当作类型注解
- 可以当作有 构造签名的函数
class Person{ }//通过Person创建一个实例对象
const p = new Person()//当作一个类型注解
function printPerson(p: Person) { }//当作一个有构造签名的函数
function factory(ctor: new () => void) { }
factory(Person)
对象类型的修饰符
- 在创建对象类型的时候,其属性也可以进行修饰
- 可选属性,在属性后面加?
- 只读属性,在属性签名加readonly
type objType = {name?:string//可选属性readonly age:number//只读属性
}
interface Iobj{name?:string//可选属性readonly age:number//只读属性
}
对象类型的索引签名
- 通常用于定义不明确对象中属性的类型
- 只能通过 string或者number进行访问
//对象类型的索引签名
interface IObj{//可以通过字符串key进行访问,value可以是number类型或者boolean类型[key: string]: number | boolean//因为length属于用string访问,其返回值应当是上面中的子类length:boolean
}//对象类型的索引签名
interface IArr{//可以通过字符串key进行访问,value可以是number类型或者boolean类型[key: number]: number | boolean//因为length属于用string访问,其返回值应当是上面中的子类length:boolean
}
接口继承
- 使用 interface定义接口,是可以通过extends继承的
- 可以减少代码量
- 使用第三方库的时候,可以使用定义好的接口,同时可以定义自己的属性
interface Iobj{name?:string//可选属性readonly age:number//只读属性
}interface Iobj2 extends Iobj{address:string
}
接口被类实现
- 定义的接口,是可以被类实现的
- 通过implements关键字进行接口的实现
- 同时一个类可以实现多个接口
interface Person{name: stringage: numberaddress: stringrunning:()=>void
}//通过字面量直接实现
let p1:Person = {name: "zhangcheng",age: 18,address: "河北省",running() {console.log("running")}
}interface IWalk {walk:()=>void
}
//通过类进行实现
//好处是可以直接通过new创建,省去了字面量创建的繁琐
//同时可以实现多个接口
class PersonClass implements Person,IWalk{constructor(public name:string,public age:number,public address:string) {this.name = namethis.age = agethis.address = address}running() {console.log("123");}walk() {console.log("456");}
}const p2 = new PersonClass("zhangcheng", 18, "河北省")
console.log(p2.running());
console.log(p2.walk());
严格字面量赋值检测
- 在TS中,有一个十分奇怪的现象
interface IPerson{name: stringage:number
}let p1:IPerson = {name: "zhangcheng",age: 18//若增加下面的属性则会报错//height:1.88
}//但是进行如下操作不会报错
let p2 = {name: "zhangcheng",age: 18,height:1.88
}
let p3: IPerson = p2function test(person: IPerson) { }test({ name: "zhangcheng", age: 18 })
//依旧不会报错
test(p2)
- 上面的代码中,我们使用interface创建了一个对象的类型
- 同时用这个类型通过字面量方式以及赋值的方式,分别创建了几个对象
- 发现通过 字面量创建的时候,会进行严格字面量的检测
- 而通过赋值的方法,则不会进行检测
- 这是因为,TS认为通过字面量创建的,就是新鲜的(这是TS成员在GitHub上面一个issue中提及的)
枚举类型
- 在TS中有一种类型叫枚举类型
- 通常用于枚举一些常量
//在其内部实际上是从0开始计数的
//我们可以改变其默认值
enum EList {UP = 100,DOWN = "DOWN",LEFT,RIGHT,
}function test(payload: EList) {switch (payload) {case EList.DOWN:console.log(123)break}
}//传递的也是其内部的值
const p: EList = EList.DOWN
test(p)