面向对象: Oriented(基于)Object(事物), 简称OO。
是一种编程思想, 他提出一切以类为切入点思考问题。
其他编程思想: 面向过程、函数式编程。
学开发最难的是什么? 思维
面向过程: 以功能流程为思考切入点,不太适合大型应用。
函数式编程: 以数学运算为思考切入点。
面向对象: 以划分类为思考切入点
类:可以产生对象的模版。类是最小的功能单元
人(类): 特征: 鼻子、眼睛、四肢、性别, 动作: 说话、运动、制造工具
new 人()
重写(override):子类中覆盖父类的成员
子类成员不能改变父类成员的类型
无论是属性还是方法,子类都可以对父类的相应成员进行重写,但是重写时, 需要保证类型的匹配
注意this关键字: 在继承关系中,this的指向是动态调用方法时,根据具体的调用者确定this指向
super关键字: 在子类的方法中,可以使用super关键字读取父类成员
鸭子辨型法
子类的对象,始终可以赋值给父类
面向对象中,这种现象,叫做里氏替换原则
如果需要判断一个数据的具体子类类型, 可以使用instanceof
readonly:只读修饰符
访问权限修饰符: private public protected
protected: 受保护的成员,只能在自身和子类中访问
ts的编译结果里没有访问修饰符
export class Tank {
name: string = "坦克"
sayHello() {
console.log(`我是一个${this.name}`)
}
}
export class PlayerTank extends Tank{
name:string = "玩家坦克"
sayHello() {
console.log('lalala')
}
test() {
super.sayHello();
this.sayHello();
}
}
export class EnemyTank extends Tank {
name:string = "敌方坦克"
}
const p: Tank = new PlayerTank();
p.sayHello();
<!我是一个玩家坦克 ---->
单根性: 每个类最多只能拥有一个父类
传递性: 如果A是B的父类,并且B是C的父类,则可以认为A也是C的父类
有时,某个类只表示一个抽象概念, 主要用于提取子类共有的成员,而不能直接创建它的对象,该类可以作为抽象类。
给类前面加上abstract
, 表示该类是一个抽象类,不可以创建一个抽象类
父类中,可能知道有哪些成员是必须存在的,但是不知道该成员的值或实现是设么,因此,需要有一种强约束,让继承该类的子类,必须要实现该成员
抽象类中, 可以有抽象成员, 这些抽象成员必须在子类中实现。
abstract class Chess{
x: number
y: number
abstract readonly name: string;
}
class Horse extends Chess{
readonly name: string = "马";
}
class Pao extends Chess{
readonly name: string;
constructor() {
super();
this.name = "炮"
}
}
class Solider extends Chess{
get name() {
return "兵"
}
}
abstract class Chess{
x: number
y: number
abstract readonly name: string;
abstract move(targetX:number, targetY:number): boolean;
}
class Horse extends Chess{
readonly name: string = "马";
move(targetX: number, targetY:number): boolean{
this.x = targetX;
this.y = targetY;
console.log("马移动成功")
return true;
}
}
class Pao extends Chess{
move(targetX: number, targetY:number): boolean{
this.x = targetX;
this.y = targetY;
console.log("炮移动成功")
return true;
}
readonly name: string;
constructor() {
super();
this.name = "炮"
}
}
class Solider extends Chess{
get name() {
return "兵"
}
move(targetX: number, targetY:number): boolean{
this.x = targetX;
this.y = targetY;
console.log("兵移动成功")
return true;
}
}
设计模式: 面对一些常见的功能场景,有一些固定的、经过多年实践的成熟方法,这些方法称之为设计模式。
模版模式: 有些方法,所有的子类实现的流程完全一致。只是流程中的某个步骤的具体实现不一致,可以将该方法提取到父类,在父类中完成整个流程的实现, 遇到实现不一致的方法时,将该方法做成抽象方法。
abstract class Chess{
x: number
y: number
abstract readonly name: string;
move(targetX:number, targetY:number): boolean {
console.log("1. 边界判断");
console.log("2. 目标位置是否有棋子")
<!3. 规则判断---->
this.rule(targetX, targetY);
};
protected abstract rule(targetX:number, targetY: number): boolean
}
class Horse extends Chess{
readonly name: string = "马";
move(targetX: number, targetY:number): boolean{
this.x = targetX;
this.y = targetY;
console.log("马移动成功")
return true;
}
}
class Pao extends Chess{
move(targetX: number, targetY:number): boolean{
this.x = targetX;
this.y = targetY;
console.log("炮移动成功")
return true;
}
readonly name: string;
constructor() {
super();
this.name = "炮"
}
}
class Solider extends Chess{
get name() {
return "兵"
}
move(targetX: number, targetY:number): boolean{
this.x = targetX;
this.y = targetY;
console.log("兵移动成功")
return true;
}
}
## 什么是静态成员
静态成员是指,附着在类上的成员(属于某个构造函数的成员)
使用static修饰的成员,是静态成员。
实例成员:对象成员,属于某个类的对象。
静态成员: 非实例成员,属于某个类
实例方法中的this指向的是当前对象
而静态方法中的this指向的是当前类
class User {
static users: User[] = [];
constructor(public loginId: string, public loginPwd: string, public name: string, public age: number) {
this.loginId = loginId;
this.loginPwd = loginPwd;
this.name = name;
this.age = age;
}
sayHello() {
console.log(`大家好,我叫${this.name}, 今年${this.age}岁了, 我的账号是${this.loginId}`)
}
static login(loginId: string, loginPwd: string) {
return this.users.find(e => e.loginId === loginId && e.loginPwd === loginPwd);
}
}
new User('u1', '123', '李', age: 23)
const result = User.login('u1', '123');
if(result) {
result.sayHello();
}
else {
console.log('登录失败,账号或密码不正确')
}
单例模式: 某些类的对象,在系统中最多只能有一个,为了避免开发者造成随意创建多个类对象的错误,可以使用单例模式进行强约束。
class Board {
width: number = 500;
height: number = 300;
init() {
console.log('初始化棋盘')
}
private constructor() {}
private static _board?: Board;
static createBoard(): Board{
if(this._board) {
return this._board;
}else {
this._board = new Board();
return this._board;
}
}
}
const b = new Board();
const b1 = Board.createBoard();
接口用于约束类、对象、函数, 是一个类型契约。
有一个马戏团,马戏团中有很多动物, 包括:狮子、老虎、猴子、狗,这些动物都具有共同的特征: 名字、年龄、种类名称,还包含一个共同的方法: 打招呼,它们各自有各自的技能, 技能是可以通过训练改变的。狮子和老虎能进行
火圈表演,猴子能进行平衡表演,狗能进行智慧表演。
class Animal {
abstract type: string
consuctor(name: string, age: number) {
this.name = name
this.age = age
}
sayHello() {
console.log(`大家好,我是${this.type},我的名字是${this.name}`)
}
}
class Lion extends Animal{
type: string = '狮子';
oneFire() {
}
doubleFire() {}
}
class Tiger extends Animal {
type: string = '老虎';
oneFire() {
}
doubleFire() {
}
}
class Monkey extends Animal{
type: string = '猴子';
banlance() {
}
}
class Dog extends Animal{
type: string = '狗';
wisdom() {
}
}
var animals: Animal[] = [
new Lion('李四', 12),
new Tiger('李敌', 4),
new Monkey('李空', 5),
new Dog('李昂', 18)
];
animals.forEach(e => e.sayHello());
animals.forEach(e => {
if(e instanceof Lion) {
e.oneFire();
e.doubleFire();
}
})
interface IShowFire {
oneFire:() => void,
doubleFire: () => void
}
class Lion extends Animal implements IShowFiere{
type: string = '狮子';
oneFire() {
}
doubleFire() {
}
}
function hasShowFire(ani:object) : ani is IShowFire{
if((ani as IShowFire).oneFire && (ani as IShowFire).doubleFire)) {
return true;
}
return false;
}
animals.forEach(e => {
if(hasShowFire(e)) {
e.oneFire();
e.doubleFire();
}
})
不使用接口实现时:
系统中缺少对能力的接口 ———— 接口
面向对象领域中的语义: 表达了某个类是否拥有某种能力
某个类具有某种能力,其实,就是实现了某种接口
类型保护函数: 通过调用该函数,会触发Ts的类型保护,该函数必须返回boolean
接口和类型别名的最大区别: 接口可以被类实现,而类型别名不可以
接口可以继承类,表示该类的所有成员都在接口中。
对象[值]
, 使用成员表达式
在TS中,默认情况下,不对索引(成员表达式)做严格的类型检查
使用配置noImplicitAny
开启对隐式any的检查
隐式any:TS根据实际情况推导出的any类型
在索引器中,键的类型可以是字符串,也可以是数字
在类中,索引器书写的位置应该是所有成员之前
TS中索引器的作用
在JS中,所有的成员名本质上,都是字符串,如果使用数字作为成员名,会自动转化为字符串。
在TS中,如果某个类中使用了两种类型的索引器, 要求两种所引起的值类型必须匹配(相同)。