面向对象概述
- TS 为前端面向对象带来了好处,JS 没有类型检查,如果使用面向对象的方式开发,会产生大量的接口,而大量的接口会导致调用复杂度剧增,这种复杂度必须通过严格的类型检查来避免错误。
- 面向对象中有许多非常成熟的模式,能处理复杂问题
类的继承
- 可以覆盖父类中的同名属性、方法,但是不能覆盖类型,类型需要跟父类保持一致
- 类型匹配:子类的对象始终可以赋值给父类,但是赋值后只能使用父类的成员
class Person {
name: ''
}
class Child {
age: 20
}
let ch:Person = new Child() // ch.age 不能使用,只能使用 ch.name,需要使用类型保护,如下:
if(ch instanceOf Child) {
console.log(ch.age )
}
- 修饰符号:protected 只能在自身和子类内部访问
- supter 关键字:在子类的方法中,可以使用 super.xxx 关键字读取父类成员
- 单根性和传递性:单根性是指:每个类最多只能拥有一个父类,传递性是指:如果 A 是 B 的父类, 并且 B 是 C 的父类,那么可以认为 A 是 C 的父类。
抽象类
有时,某个类只表示一个抽象概念,主要用于提取子类共有的成员,而不能直接创建它的对象,该类可以作为抽象类,其他类可以继承它
abstract class Person {}
如何抽象成员:父类中可能知道有些成员是必须存在的,但是不知道该成员的直或实现是什么,因此,需要有一种强约束,让继承该类的必须要实现该成员。 直接给成员加上 abstract。
abstract class Person {
abstract name:string
abstract age:number
abstract height:number
}
class child extends Person {
constructor(public name:string, public age:number, public height:number){
super()
}
}
抽象类也可以继承抽象类,而且可以不用实现父类的方法,让继承它的非抽象子类去实现
静态成员
静态成语是指,附着在类上的成员,可以直接通过类.xxx 来访问的成员,不用通过创建实例,并且在实例中也访问不到。
class Person {
static walk(step):void {
console.log('walk' + ' ' + step )
}
}
Person.walk(10)
静态函数的 this 指向当前类不是实例
单例模式
class Person {
private constructor(){} // 将构造函数私有化,那么就不能再外部调用 new 来实例化
static readonly _Person:Person = new Person()
// 上面的代码就算在不需要的时候也会在初始化的时候创建,可以像下面这样
static _Person:Person
init():Person{
if(Person._Person) {
return Person._Person
}
Person._Person = new Person()
return Person._Person
}
}
再谈接口
面向对象领域中的接口的用处是用来表达某个类是否拥有某种能力
interface Language {
speakEnglish():void,
speakChinese():void
}
class Person {
constructor(
public name:string,
public age:number
) {}
}
class Child extends Person implements Language {
speakEnglish(): void {
}
speakChinese(): void {
}
}
接口也可以继承类,接口可以多继承
class A extends B, C {}
索引器
class A { name: 'xxx' }
const a = new A()
console.log(a.name) //1
console.log(a['test'])//2
对于第二种写法 Ts 不会做类型检查,如果想要开启这种检查,需要设置 "noImpliciAny": true
,开启对隐式 any 类型的检查。默认情况下如果不开启,那么第二种判断不出到底是什么什么类型时(这里是一个常量,有可能是一个变量或者函数等不可预知的),那么会使用 any 类型,这个时候就会通过 TS 的类型检查。开启之后就会对这些情况进行报错。但是这个配置也不仅会影响到索引器,其他地方也会受到更严格的检查,例如声明函数时,参数未指明类型也会报错。
如果想开启动态添加属性,即添加一个类中没有的属性,可以像下面这样写
class A {
[prop:string]:any // 用来约束所有的属性, [] 里面的表示键的类型,: 后面的表示属性值的类型,这个就叫索引器,需要放到类的顶部
}
const a = new A()
a.b = 1
a['c'] = 2
this 指向约束
ES6 中的类的内部使用的是严格模式,因此内存的函数如果没有通过 实例.xxx 去调用,那么默认是 undefined。在 TS 中默认对于用字面量声明的对象里函数的 this,TS 认为是 any 类型,而 class 内部的 this 类型认为是当前类.
Ts 中提供了一种选项,配置 noImplicitThis 为 true 后,表示不允许 this 隐式的指向 any,即需要声明 this 的指向,因此如果在书写函数时,在内部使用了 this,需要声明该函数 this 的指向,将 this 作为函数的第一个参数,如下
interface Func {
name: string,
age: number,
say(this:Func):void,
talk(this:Func): void,
}
let func:Func = {
name: 'fjdslk',
age: 13,
say() { // 不要写箭头函数,因为箭头函数的 this 是跟当前上下文有关,无法约束
console.log(this.name)
},
talk() {
console.log(this.name)
}
}
let say = func.say // this 丢失
say()// 开启后这里会报错,因为 this 指向错误
class A {
name:string = 'fjldsfd'
say(this:A) {
console.log(this.name)
}
}
let a = new A()
const say = a.say
say() // 同样的这里也会报错,因为 this 指向错误