#

ts的类和ES6的类类似,constructor为构造函数,在里面接收默认参数或执行默认函数,属于实例本身。在constructor外的其他方法则相当于定义在原型上。

class Person {
  name: string
  constructor(name: string) {  // 构造函数 实例化的时候触发的方法
    this.name = name
  }
  run():void {
    console.log(this.name)
  },
  eat(food: string):void {
    console.log(`${this.name} like eat ${food}`)
  }
}

var p = new Person('chang');
p.run(); // chang
p.eat('banana'); // chang like eat banana

# 类的修饰符

类的修饰符主要有public protected private readonly,默认为public,对类内部的属性或方法进行修饰,以达到约束使用范围。

  • public: 公有属性或方法 子类、类外面都可以访问,默认为public
  • protected:保护类型 在类、子类里面可以访问,类外部无法访问,实例化后也无法使用
  • private: 私有属性或方法 只在类里面可以访问,子类、类外部或实例都无法访问
  • readonly:只读属性,无法修改
class Animal {
  readonly id: number
  public name:string
  protected level:number
  private age: number
  constructor(name:string, level: number, age:number) {
    this.name = name
    this.level = level
    this.age = age
  }
  eat():void {
    console.log(`${this.name}在吃东西`)
  }
  getAge():void {
    console.log(`${this.age}在吃东西`)
  }
}

class Cat extends Animal {
  constructor(name:string, level: number, age:number) {
    super(name, level,age)
  }
  move():void {
    console.log(`${this.name}在玩耍`)
  }
  getLevel():void {
    console.log(this.level)
  }
}

var animal = new Animal('动物', 0, 3)
console.log(animal.name)  // 动物   在外部访问公有属性
// console.log(animal.level)  // 保护类型在外部无法访问
// console.log(animal.age)  // 私有类型无法访问

var cat = new Cat('猫', 1, 18)
cat.eat()
cat.move();
cat.getLevel();  // 1  内部获取protected类型

console.log(cat.name)  // 猫
// console.log(cat.age)  // 私有类型无法访问
// console.log(cat.level)  外部无法访问protected类型

# 参数属性

在类定义一个变量,然后在constructor中接收传入的参数,每次都这样写太麻烦,可以直接在constructor接收参数时定义参数的修饰符来简写。

class Department {
  public name:string
  protected level:number
  private age: number
  constructor(name:string, level: number, age:number) {
    this.name = name
    this.level = level
    this.age = age
  }
}
// 等同于
class Department1 {
  constructor(public name:string, protected level: number, private age:number) {
  }
}

# 静态属性、静态方法

类可以直接调用,实例对象无法调用。静态方法的this只能调用类的静态属性。

class Tool {
  name: string = 'chang'
  static age = 200 // 静态属性
  constructor() {}
  static getAge() {  // 静态方法
    console.log(this.age)
  }
}

Tool.getAge();  // 200
console.log(Tool.age) // 200

# getter和setter

使用get函数和set函数来对某个属性进行取值和存值,并拦截该属性的存取行为。通过 “.” 操作获取和设置值。

class Phone {
  num = 0
  // 通过return来取值
  get price() {
    console.log('价格属性获取成功');
    return this.num
  }
  // 通过传递参数来存值
  set price(newValue) {
    this.num = newValue
    console.log('价格属性设置成功');
  }
}

var p = new Phone()
p.price
// 价格属性获取成功
// 0
p.price = 180
// 价格属性设置成功
// 180
console.log(p.price) 
// 180

# 类的约束

定义一个接口对类进行约束,使用implements关键字来定义

interface DogAttr {
  food: string
  eat(str?:string):void
}

// 类必须要有name和eat
class Dog implements DogAttr {
  food:string
  constructor(food:string) {
    this.food = food
  }
  eat():void {
    console.log(`dog like eat ${food}`)
  }
}

var dog = new Dog('狗粮')
dog.eat();

# 继承

子类继承父类的方法和属性,使用 extends 继承后,必须在子类的constructor函数中执行super后才能继承父类,不执行会报错,并且super必须放在最上面才行。

super的原理就是 prototype.constructor.call 借用父类里的方法和属性。

class Person {
  name: string
  age: number
  constructor(name: string, age: number) {  // 构造函数 实例化的时候触发的方法
    this.name = name
    this.age = age
  }
  run():void {
    console.log(this.name)
  }
}
// extends super
class Student extends Person {
  // 通过super继承父类的属性和方法
  constructor(name: string) {
    super(name, 18) // 参数可以子类传入,也直接传
    super.run() // 可以使用super直接调用父类的方法或属性
  }
  learn():void {
    console.log(`${this.name}学习中`)
  }
}
var stu = new Student('李四')
stu.run();
stu.learn();

# 多态

父类定义一个方法不去实现,让继承他的子类去实现,每个子类有不同的表现

class Animal2 {
  name: string
  constructor(name:string) {
    this.name = name
  }
  eat() {
    console.log('吃')
  }
}

class Dog extends Animal2 {
  constructor(name: string) {
    super(name)
  }
  eat() {
    console.log(this.name + '吃骨头')
  }
}

class Cat1 extends Animal2 {
  constructor(name: string) {
    super(name)
  }
  eat() {
    console.log(this.name + '吃鱼')
  }
}

# 抽象类

抽象类也叫做基类(鸡肋),抽象类它是提供其他类继承的基类,不能被直接实例化,在抽象类里定义的方法叫抽象方法,抽象方法只能放在抽象类中,让子类必须定义。就像类的接口一样对类进行约束,但抽象类里可以定义其他方法,子类可以直接使用,比接口更高级一点。

abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体事项并且必须在派生类中实现

抽象类和抽象方法用来定义标准:比如: Animal3 这个类要求他的子类必须包括eat方法

// 抽象类/基类
abstract class Animal3 {
  name:string
  constructor(name: string) {
    this.name = name
  }
  jump():void {}
  // 定义一个抽象方法,继承他的子类必须实现
  abstract eat():any;
}

// var animal = new Animal3('name')  // 不能实例化

// 派生类
class Pig extends Animal3 {

  constructor(name: any) {
    super(name)
  }
  run() { }
  eat():void {
    console.log(`${this.name}会吃`)
  }
}

var pig = new Pig('猪')
pig.jump();
pig.eat();
pig.run();

# 虚拟DOM

模仿Vue创建和渲染虚拟DOM

// 定义传参options的接口
interface Options {
  el: string | HTMLElement
}

// 定义类接口
interface VueCls {
  options: Options
  init():void
}

// 定义节点的接口
interface Vnode {
  tag: string
  text?: string
  children?: Vnode[]
}

// 创建一个父类,定义一些渲染DOM的方法
class Dom {
  // 创建节点
  private createElement(el: string) {
    return document.createElement(el);
  }
  // 填充文本
  private fillText(el: HTMLElement, text: string | null) {
    el.textContent = text
  }
  // 渲染DOM节点,传入虚拟节点,递归创建DOM,然后返回
  protected render(data:Vnode) {
    let dom: HTMLElement = this.createElement(data.tag)
    if (data.text) this.fillText(dom, data.text)
    if (data.children && data.children.length) {
      data.children.forEach(item => {
        let childDom = this.render(item)
        dom.appendChild(childDom)
      })
    }
    return dom
  }
}

// 创建一个Vue
class Vue extends Dom implements VueCls {
  options: Options
  constructor(options: Options) {
    super() // 继承父类
    this.options = options
    // 默认执行方法
    this.init()
  }
  init(): void {
    // 定义一个虚拟DOM,然后通过js渲染成真正的DOM
    let data:Vnode = {
      tag: 'div',
      children: [
        {
          tag: 'p',
          text: '我是子节点1'
        },
        {
          tag: 'p',
          text: '我是子节点2',
          children: [
            {
              tag: 'span',
              text: '我是子节点2-1',
            }
          ]
        }
      ]
    }
    // 获取渲染成功后的DOM添加到根节点
    let dom = this.render(data)
    let app = typeof this.options.el === 'string' ? document.querySelector(this.options.el) : this.options.el
    app?.appendChild(dom)
  }
}

new Vue({
  el: '#app'
})