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{
//可以自由访问,默认就是public
public 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: string
constructor(name:string) {
this.name = name,
}
}
getters/setters
- 主要是对私有属性,进行设置
- 其目的是为了拦截,防止一些非法的操作发生
class Person{
private _age: number
constructor(_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: number
constructor(_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: string
age: number
address: string
running:()=>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 = name
this.age = age
this.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: string
age:number
}
let p1:IPerson = {
name: "zhangcheng",
age: 18
//若增加下面的属性则会报错
//height:1.88
}
//但是进行如下操作不会报错
let p2 = {
name: "zhangcheng",
age: 18,
height:1.88
}
let p3: IPerson = p2
function 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)