29、TS中的类
  jGAU8NKRKR9k 2023年12月08日 12 0



TS中的类

  • js中的类的演变史
  • 类的概念
  • 类在ts中如何进行类型声明
  • ES6中类的用法
  • 属性方法
  • 类的继承
  • 类的存取器
  • 静态方法
  • ES7中类的用法
  • 实例属性
  • 静态属性
  • Typescript中类的用法
  • public 修饰的属性或方法是公有的,默认所有的属性和方法都是 public 的
  • private 修饰的属性或方法是私有的,不能在声明它的类的外部访问
  • protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的
  • constructor标记被private标记,既不能被实例化,也不能被继承
  • 当构造函数修饰为 protected 时,该类只允许被继承不可以被实例化(继承后的类可以实例化)
  • 参数属性
  • 写法简化
  • 只读属性
  • 抽象类
  • 抽象类不可以实例化
  • 抽象类中的抽象方法必须被子类实现
  • 类的类型


js中的类的演变史

类的概念在js早起是并不存在,所以实现面向对象编程是比较困难的,那个阶段称之为刀耕火种吧。

简单概括一下:传统方法中,JavaScript 通过工厂函数,构造函数 实现类的概念,通过原型链实现继承。
在 ES6 中,我们终于迎来了 class。

TypeScript 除了实现了所有 ES6 中的类的功能以外,还添加了一些新的用法。

演变史参看这篇文章


类的概念

概念写在前,提前记

  1. 类(Class):定义了一件事物的抽象特点,包含它的属性和方法
  2. 对象(Object):类的实例,通过 new 生成
  3. 面向对象(OOP)的三大特性:封装、继承、多态
  4. 封装(Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要(也不可能)知道细节,就能通过对外提供的接口来访问该对象,同时也保证了外界无法任意更改对象内部的数据
  5. 继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性
  6. 多态(Polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应。比如 Cat 和 Dog 都继承自 Animal,但是分别实现了自己的 eat 方法。此时针对某一个实例,我们无需了解它是 Cat 还是 Dog,就可以直接调用 eat 方法,程序会自动判断出来应该如何执行 eat(这个eat可以是继承过来的,同时子类也可以对这个方法进行重写)。
  7. 存取器(getter & setter):用以改变属性的读取和赋值行为
  8. 修饰符(Modifiers):修饰符是一些关键字,用于限定成员或类型的性质。比如 public 表示公有属性或方法
  9. 抽象类(Abstract Class):抽象类是供其他类继承的基类,抽象类不允许被实例化。抽象类中的抽象方法必须在子类中被实现
  10. 接口(Interfaces):不同类之间公有的属性或方法,可以抽象成一个接口。接口可以被类实现(implements)。一个类只能继承自另一个类,但是可以实现多个接口

类在ts中如何进行类型声明

先熟悉知道这种写法,后面写的时候规范写

举个demo:

class Animal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  sayHi() {
    return `My name is ${this.name}`
  }
}
let mq: Animal = new Animal('Jack')
console.log(mq.sayHi())

编译后:

"use strict";
class Animal {
    constructor(name) {
        this.name = name;
    }
    sayHi() {
        return `My name is ${this.name}`;
    }
}
let mq = new Animal('Jack');
console.log(mq.sayHi());

ES6中类的用法

详细参阅阮一峰大佬的文章

属性方法

1、使用class定义类,使用constructor定义构造函数
2、通过new生成新实例的时候,会自动调用构造函数

class Animal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  sayHi() {
    return `My name is ${this.name}`
  }
}
let mq: Animal = new Animal('Jack')
console.log(mq.sayHi()) // My name is Jack

类的继承

0、类的继承
1、尝试去打印super,但是报错:‘super’ keyword unexpected here
2、可以看到,继承的话,在父类中已有的方法,可以在子类中重写,Lion 就重写了Animal 的sayHi方法
3、super在子类的constructor函数中调用,可以直接当做是父类的constructor方法使用
4、super在子类的其余地方调用,可以理解为父类的实例对象,可以调用父类的方法

class Animal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  sayHi() {
    return `My name is ${this.name}`
  }
}
let mq: Animal = new Animal('Jack')
console.log(mq.sayHi()) // My name is Jack

class Lion extends Animal {
  constructor(name: string) {
    super(name)
  }
  sayHi() {
    // console.log(super)
    return `吼,` + super.sayHi()
  }
}
let lio: Lion = new Lion('狮子')
console.log(lio.sayHi()) // 吼,My name is 狮子

类的存取器

仔细看存取器,存取器可不是方法,不能供实例调用的,实例去调用会报错的

class Tiger {
  public _name: string
  constructor(name: string) {
    this._name = name
  }
  get name() {
    return this._name
  }
  set name(name: string) {
    this._name = name
  }
}
let tiger = new Tiger('老虎')
console.log(tiger._name) // 老虎
tiger._name = '狐狸'
console.log(tiger._name) // 狐狸
// tiger.set('dog'); // 会报错的
console.log(tiger._name) // 狐狸

静态方法

类的静态方法在类上,类可以调用;实例是不行的

class Dolphin {
  static swim(obj: object) {
   return obj instanceof Dolphin;
  }
}
let dolphin = new Dolphin();
console.log(dolphin) // {}
let result = Dolphin.swim(dolphin)
console.log(result) // true
dolphin.swim(dolphin) // 直接报错了:dolphin.swim is not a function

ES7中类的用法

es7中有一些关于类的提案,ts也实现了他们

实例属性

es6中实例属性智能通过this.xxx来定义,es7中提案中可以直接在类里面定义

class Apple {
  name = 'apple';
  constructor () {

  }
}
let apple = new Apple()
console.log(apple.name) // apple

静态属性

class Peach {
  static color = "pink";
  constructor() {

  }
}
console.log(Peach.color) // “pink”

Typescript中类的用法

TypeScript 可以使用三种访问修饰符(Access Modifiers),分别是 public、private 和 protected

public 修饰的属性或方法是公有的,默认所有的属性和方法都是 public 的

name 被设置为了 public,所以直接访问实例的 name 属性是允许的

class Banana {
  public name;
  constructor (name: string) {
    this.name = name
  }
} 
const banana = new Banana('xiangjiao')
console.log(Banana.name) // Banana--类的名字
console.log(banana.name) // xiangjiao
banana.name = "xj"
console.log(banana.name) // xj

class Xj extends Banana {
  constructor(name: string) {
    super(name)
  }
}
const xxjj = new Xj('香蕉')
console.log(xxjj.name) // 香蕉
console.log(Xj.name) // Xj--类的名字

private 修饰的属性或方法是私有的,不能在声明它的类的外部访问

有些时候,我们希望有的属性是无法直接存取的,这时候就可以用 private 了

class Watermelon {
  private name;
  constructor(name: string) {
    this.name = name
  }
}
let w = new Watermelon('xigua')
console.log(w.name) // Property 'name' is private and only accessible within class 'Watermelon'.
w.name = '西瓜' //  Property 'name' is private and only accessible within class 'Watermelon'.

上述代码编译后:

class Watermelon {
    constructor(name) {
        this.name = name;
    }
}
let w = new Watermelon('xigua');
console.log(w.name); // xigua
w.name = '西瓜';
console.log(w.name); // 西瓜

通过编译后的代码可以得出结论:TypeScript 编译之后的代码中,并没有限制 private 属性在外部的可访问性,private 仅在ts编译阶段报错,最终js的执行并不会报错。

private 修饰的属性在子类的中也是不允许访问的(同样不会影响编译后的js运行)

class Watermelon {
  private name;
  constructor(name: string) {
    this.name = name
  }
}
class Smallwatermelon extends Watermelon {
  constructor(name: string) {
    super(name)
    // console.log(this.name) Property 'name' is private and only accessible within class 'Watermelon'.
  }
}
const k = new Smallwatermelon('xigua1')
console.log(k.name) // Property 'name' is private and only accessible within class 'Watermelon'.

// -----编译后的代码-----
class Watermelon {
    constructor(name) {
        this.name = name;
    }
}
class Smallwatermelon extends Watermelon {
    constructor(name) {
        super(name);
    }
}
const k = new Smallwatermelon('xigua1');
console.log(k.name); // xigua1

protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的

class Book {
  protected name;
  constructor(name:string) {
    this.name = name
  }
}
const money = new Book('小狗钱钱')
// console.log(money.name) // Property 'name' is protected and only accessible within class 'Book' and its subclasses.

class Xiyouji extends Book {
  constructor(name:string) {
    super('西游记')
    console.log(this.name) // 不会报错
  }
}
const xyj = new Xiyouji('西游记')
// console.log(xyj.name) // Property 'name' is protected and only accessible within class 'Book' and its subclasses

constructor标记被private标记,既不能被实例化,也不能被继承

class Kate {
  public name;
  private constructor(name: string) {
    this.name = name
  }
}
class Jake extends Kate {  //  Cannot extend a class 'Kate'. Class constructor is marked as private.
  constructor(name: string) {
    super(name)
  }
}
let lade = new Kate('lade')  // Constructor of class 'Kate' is private and only accessible within the class declaration

//----编译后的代码(可以看到编译后,其实是不受这个影响的)---
class Kate {
    constructor(name) {
        this.name = name;
    }
}
class Jake extends Kate {
    constructor(name) {
        super(name);
    }
}
let lade = new Kate('lade');

当构造函数修饰为 protected 时,该类只允许被继承不可以被实例化(继承后的类可以实例化)

class Kate {
  public name;
  protected constructor(name: string) {
    this.name = name
  }
}
class Jake extends Kate {
  constructor(name: string) {
    super(name)
  }
}
let uu = new Jake('jake') // 继承来的类是可以实例化的
console.log(uu.name) // jake
let lade = new Kate('lade') // Constructor of class 'Kate' is protected and only accessible within the class declaration.

参数属性

写法简化

修饰符和readonly还可以使用在构造函数参数中,等同于类中定义该属性同时给该属性赋值,使代码更简洁。

class Zkf {
  // public name;
  public constructor(public name: string) {
    // this.name = name
  }
}

编译过后的js如下:

class Zkf {
    // public name;
    constructor(name) {
        this.name = name;
        // this.name = name
    }
}

只读属性

只读属性关键字,只允许出现在属性声明或索引签名或构造函数中

class Furniture {
  readonly name;
  public constructor(name:string) {
    this.name = name
  }
}
let x = new Furniture('desk')
console.log(x.name) // desk
x.name = 'floor' // 报错:Cannot assign to 'name' because it is a read-only property.

编译后的js文件

class Furniture {
    constructor(name) {
        this.name = name;
    }
}
let x = new Furniture('desk');
console.log(x.name); // desk
x.name = 'floor';
console.log(x.name); // floor

注意如果 readonly 和其他访问修饰符同时存在的话,需要写在其后面。

class Botany {
  public constructor(public readonly name: any) {

  }
}

抽象类

abstract 用于定义抽象类和其中的抽象方法

抽象类不可以实例化

abstract class Food {
  public name;
  public constructor(name:string) {
    this.name = name
  }
}
let rice = new Food('rice') //  Cannot create an instance of an abstract class

// 编译后的js
class Food {
    constructor(name) {
        this.name = name;
    }
}
let rice = new Food('rice');

抽象类中的抽象方法必须被子类实现

abstract class Food {
  public name;
  public constructor(name:string) {
    this.name = name
  }
  public abstract sayHi(): any;
}
class Mianbao extends Food{
  public eat() {
    console.log(`${this.name} is eat`)
  }
}
// 编译报错:  Non-abstract class 'Mianbao' does not implement all abstract members of 'Food'

// 如下便不会报错
abstract class Food {
  public name;
  public constructor(name:string) {
    this.name = name
  }
  public abstract sayHi(): any;
}
class Mianbao extends Food{
  public eat() {
    console.log(`${this.name} is eat`)
  }
  public sayHi() {
    console.log('sayhi')
  }
}

正确demo:

abstract class Food {
  public name;
  public constructor(name:string) {
    this.name = name
  }
  public abstract sayHi(): any;
}
class Mianbao extends Food{
  public eat() {
    console.log(`${this.name} is eat`)
  }
  public sayHi() {
    console.log(`${this.name} is my name`)
  }
}
let ff = new Mianbao('面包')
console.log(ff.name) // 面包

类的类型

class Vehicle {
  name: string;
  constructor(name: string) {
    this.name = name
  }
  sayHi(): string {
    return `my name is ${this.name}`
  }
}
let bic: Vehicle = new Vehicle('bicycle')
console.log(bic.sayHi()) // my name is bicycle


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年12月08日 0

暂无评论

推荐阅读
jGAU8NKRKR9k