Skip to content

JavaScript 和 TypeScript 类实践

JavaScript 类

js
/**
 * JS类
 * 1.如何定义类的属性和方法(静态, 类字段, 普通, 私有)
 * 2.如何继承类
 * 3.如何使用父类的属性和方法
 * 4.抽象类
 */
class Father {
  // 1.静态属性和静态方法, 直接使用类来访问: Father.name和Father.sayName(), 实例对象无法访问
  static sname = 'george'
  static sayName() {
    return `我是爸爸,我叫 ${this.sname}`
  }
  // 2.类字段和方法(语法糖,直接赋值),和constructor类似, 但是不能传参和写逻辑
  // 实例对象使用,不能通过类直接访问
  age = 40
  sayAge = () => {
    // 使用箭头函数,this自动绑定到当前实例
    return `我是爸爸, 我今年 ${this.age}`
  }

  // 3.构造函数和实例方法(普通方法), 使用constructor,适合传参和写逻辑
  constructor(x, y) {
    this.x = x
    this.y = y || 0
  }
  sum() {
    // 使用实例参数
    return this.x + this.y
  }
  moreSum(m) {
    // 使用外部参数
    return this.sum() + m
  }

  // 4.私有属性和方法,只能类內部使用
  #id = 888888
  #showId() {
    return this.#id
  }
  sayId() {
    return `我是爸爸,我的ID是 ${this.#showId()}`
  }
}

// 调用Father的静态属性和方法
console.log(`类Father的静态属性, 由Father直接访问name=>${Father.name}`)
console.log(`类Father的静态方法, 由Father直接调用sayName=>${Father.sayName()}`)

// 实例化Father类
const newFather = new Father(1, 2)
// 静态属性: 实例newFather直接访问类Father的静态属性,会失败
console.log(`实例对象newFather尝试访问类Father的静态属性name=>${newFather.name}`) // 类的静态属性实例不能访问:undefined
console.log(`实例对象nweFather尝试访问Father的静态方法=>${newFather.sayName}`) // 类的静态方法实例不能访问:undefined(调用会报错)
// 类字段: 实例newFather可以访问类字段属性和方法
console.log(`实例对象newFather尝试访问Father的类字段age=>${newFather.age}`)
console.log(`实例对象nweFather尝试访问Father的类方法sayAge=>${newFather.sayAge()}`)
// 普通: 实例newFather可以访问普通属性和方法
console.log(`实例对象newFather尝试访问Father的普通属性x=>${newFather.x}`)
console.log(`实例对象newFather尝试访问Father的普通方法sum=>${newFather.sum()}`)
console.log(`实例对象newFather尝试访问Father的普通方法moreSum=>${newFather.moreSum(3)}`) // 使用了外部传参的普通方法
// 私有: 实例newFather不可以访问私有属性和方法
try {
  console.log(`实例对象newFather尝试访问Father的私有属性id=>${id}`)
  console.log(`实例对象newFather尝试访问Father的私有方法showId=>${showId()}`)
} catch {
  console.log('私有属性id和方法showId不可访问')
}
console.log('//////////////////////////////////////////////////////////////////////////////')

// Son继承Father的属性和方法
class Son extends Father {
  // 子类自己的静态方法调用父类的静态属性和方法
  static sonSelf() {
    return `我叫 ${super.sname}, ${super.sayName()}`
  }
  constructor(x, z) {
    super(x) // super必须在最前面, super 就是父类,用来调用父类的构造函数(属性)或方法(将实参传递给父类)
    // super(x + 1)  // 可以对参数进行加工,将影响子类调用父类的结果
    // this.x = x // 如果Son想保留传入的实参值,可以保留
    this.z = z
    this.sayHi(`实例化时就调用:正在实例化Son类, 传入的实参为 ${this.x} 和 ${this.z}`)
  }
  sayHi(hi) {
    console.log(hi)
  }
  // 子类自己的普通方法,调用父类普通的sum方法和age属性
  subtract() {
    return super.sum() + this.age - this.z
  }
  // 覆写父类的方法
  sayAge = () => {
    // 直接重写覆盖即可, 箭头函数就要箭头函数覆盖, 普通函数就要普通函数覆盖
    return `我是儿子, 我今年 ${this.age - 24}`
  }
}

console.log(`Son实例访问Father类的静态属性和方法=>${Son.sonSelf()}`)
//  实例化Son类
const s = new Son(5, 3) // 只是用了父类的x和子类的z形参
console.log(`Son实例对象访问Father类的方法sum=> ${s.sum()}`)
console.log(`Son实例访问Son类的普通方法subtract=>${s.subtract()}`)
console.log(`Son实例访问Son类的覆写类字段方法sayAge=>${s.sayAge()}`)

TypeScript 类

js
// 接口是用来暴露公开能力的,静态的,私有的属性和方法都不用定义接口
interface ITsFather {
  // static name:string 不能直接显示类的静态属性和方法
  age: number
  sayAge(): string
  x: number
  sum(): number
}

class TsFather implements ITsFather {
  // 1.静态方法和属性
  static sname: string = 'Tom'
  static sayName(): string {
    return `我是父类,我的名字叫 ${this.sname}`
  }
  // 2.类字段和方法
  public age: number = 42
  sayAge = (): string => {
    return `我今年 ${this.age}岁了`
  }
  // 3.constructor构造函数
  constructor(
    public x: number, // 公开属性
    protected y: number, // 受保护属性, 允许在类的内部和子类中访问,但外部依然不应该访问
    private z: number // 私有属性,只允许在类的内部访问,子类都不能访问
  ) {}
  // 普通方法
  sum(): number {
    return this.x + this.y + this.z
  }
}

// 访问静态属性和方法
console.log(TsFather.sname)
console.log(TsFather.sayName())

// 实例化类
const tf = new TsFather(1, 2, 3)
// 访问实例的类字段属性和方法
console.log(tf.age)
console.log(tf.sayAge())
// 访问实例的普通属性和方法
console.log(tf.x)
// console.log(tf.y) // 静态检查不通过,实际能运行
// console.log(tf.z) // 静态检查不通过,实际能运行

console.log('/////////////////////////////////////////////////////')
// 类的继承
class TsSon extends TsFather {
  // 子类自己的静态方法调用父类的静态属性和方法
  static sonSelf() {
    return `我是子类,正在访问父类的属性和方法:我叫 ${super.sname}, ${super.sayName()}`
  }
  constructor(x: number, y: number, z: number, public t: number) {
    super(x, y, z) // super必须在最前面, super 就是父类,用来调用父类的构造函数(属性)或方法(将实参传递给父类)
    // super(x + 1)  // 可以对参数进行加工,将影响子类调用父类的结果
    // this.x = x // 如果Son想保留传入的实参值,可以保留
    this.t = t
    this.sayHi(`实例化时就调用:正在实例化TsSon类, 传入的实参为 ${this.x} 和 ${this.t}`)
  }
  sayHi(hi: string): void {
    console.log(hi)
  }
  // 子类自己的普通方法,调用父类普通的sum方法和age属性
  subtract() {
    return super.sum() + this.age - this.t
  }
  // 覆写父类的方法
  override sayAge = () => {
    // override关键字,重写方法覆盖即可, 箭头函数就要箭头函数覆盖, 普通函数就要普通函数覆盖
    return `我是儿子, 我今年 ${this.age - 24}`
  }
}

// 访问父类的属性和方法
console.log(TsSon.sname)
console.log(TsSon.sayName())
// 访问子类的静态属性和方法
console.log(TsSon.sonSelf())

// 实例化子类
const ts = new TsSon(1, 2, 3, 4)
// 访问子类覆写的父类方法
console.log(ts.subtract())