# class 定义方式

  • 我们会发现,按照前面的构造函数形式创建 类,不仅仅和编写普通的函数过于相似,而且代码并不容易理解。
    • 在 ES6(ECMAScript2015)新的标准中使用了 class 关键字来直接定义类;
    • 但是类本质上依然是前面所讲的构造函数、原型链的语法糖而已;
    • 所以学好了前面的构造函数、原型链更有利于我们理解类的概念和继承关系;
  • 那么,如何使用 class 来定义一个类呢?
    • 可以使用两种方式来声明类:类声明和类表达式;
// 类的声明
class Person {}
// 类的表达式
// const Animal = class {}
// 研究一下类的特性
console.log(Person.prototype) // {constructor: ƒ}/node -> {}
console.log(Person.prototype.__proto__) // constructor: ƒ, …}/node -> [Object: null prototype] {}
console.log(Person.prototype.constructor) // [class Person]
console.log(typeof Person) // function
var p = new Person()
console.log(p.__proto__ === Person.prototype) // true

# class 构造函数

  • 如果我们希望在创建对象的时候给类传递一些参数,这个时候应该如何做呢?

    • 每个类都可以有一个自己的构造函数(方法),这个方法的名称是固定的 constructor;
    • 当我们通过 new 操作符,操作一个类的时候会调用这个类的构造函数 constructor;
    • 每个类只能有一个构造函数,如果包含多个构造函数,那么会抛出异常;
  • 当我们通过 new 关键字操作类的时候,会调用这个 constructor 函数,并且执行如下操作:

    • 在内存中创建一个新的对象(空对象);
    • 这个对象内部的 [[prototype]] 属性会被赋值为该类的 prototype 属性;
    • 构造函数内部的 this,会指向创建出来的新对象;
    • 执行构造函数的内部代码(函数体代码);
    • 如果构造函数没有返回非空对象,则返回创建出来的新对象;

# class 方法定义

const characters = ['稚名真白', '樱岛麻衣', '薇尔莉特', '和泉纱雾', '小鸟游六花' ]
// 类的实例方法
class Characters {    
  constructor(name) {
    this.name = name
    this._friends = 'saber'
  }
  sleeping(){
    console.log(this.name + ' sleeping');
  }
  // 类的访问器方法
  get friends() {
    // 拦截访问器操作
    return this._friends
  }
    // 拦截设置操作
  set friends(newFriends) {
    this._friends = newFriends
  }
  // 类的静态方法
  static randomCharacters() {
    return new Characters ((characters[~~(Math.random() * characters.length)]))
  }
}
Object.getOwnPropertyDescriptors
const lain = new Characters("lain")
console.log(lain) // Person {name: 'lain'}
lain.sleeping()
lain.friends = 'nekoaimer'
console.log(lain) // Character {name: 'lain', _friends: 'nekoaimer'}
// 随机打印创建角色
characters.forEach(() => {
  console.log(Characters.randomCharacters())
})

# class 继承与重写

  • 我们会发现在下面的代码中我使用了一个 super 关键字,这个 super 关键字有不同的使用方式:
    • 注意:在子(派生)类的构造函数中使用 this 或者返回默认对象之前,必须先通过 super 调用父类的构造函数!
    • super 的使用位置有三个:子类的构造函数、实例方法、静态方法;
class Characters {
  constructor(name) {
    this.name = name
  }
  sleeping(){
    console.log('Characters sleeping~');
  }
  static sleepingStatic() { 
    console.log(`Characters sleepingStatic~`);
  }
}
// Lain 称之为子类 (派生类)
class Lain extends Characters {
  // JS 引擎在解析子类的时候就有要求,如果我们有实现继承
  // 那么子类的构造方法中,在使用 this 之前
  constructor(name, age) {
    super(name)
    this.age = age
  }
  drinking() {
    console.log(`Lain drinking~`);
  }
  //sleeping 方法重写 
  sleeping() {
    super.sleeping()  // 调用父类方法执行逻辑再执行子类执行逻辑
    console.log(`Lain sleeping~~~`)
  }
  // 重写静态方法
  static sleepingStatic() {
    super.sleepingStatic() // 调用父类静态方法执行逻辑再执行子类静态执行逻辑
    console.log('sleepingStatic~~~');
  }
}
const lain = new Lain("lain", 16)
lain.sleeping() // Characters sleeping~ & Lain sleeping~~~
console.log(lain) // Lain {name: 'lain', age: 16}
Lain.sleepingStatic() // Characters sleepingStatic~ & sleepingStatic~~~
// console.log(Object.getOwnPropertyDescriptors(lain.__proto__))
// console.log(Object.getOwnPropertyDescriptors(lain.__proto__.__proto__))

# class 继承内置

class _Array extends Array{
  arrLength() {
   return this.length
 }
}
const arr = new _Array(1, 2, 3)
arr.arrLength() // 3

# class 实现混入

  • JavaScript 的类只支持单继承:也就是只能有一个父类
    • 那么在开发中我们我们需要在一个类中添加更多相似的功能时,应该如何来做呢?
    • 这个时候我们可以使用混入(mixin);
class Characters {
}
// 在 JS 中类只能有一个父类:单继承
class Lain extends Characters {
  
}
function mixinCharacters(BaseClass) {
  return class extends BaseClass {
    sleeping() {
      console.log("sleeping~")
    }
  }
}
const NewLain = mixinCharacters(Lain)
const lain = new NewLain()
lain.sleeping()

# JavaScript 多态

  • 传统的面向对象多态是有三个前提:

  • 必须有继承 (是多态的前提)

  • 必须有重写 (子类重写父类的方法)

  • 必须有父类引用指向子类对象

# TS 多态

class Characters {
  getFriends() {
    return '伊莉雅'
  }
}
class Lain extends Characters {
  getFriends() {
    return '樱岛麻衣'
  }
}
class Saber extends Characters {
  getFriends() {
    return '小鸟游六花'
  }
}
var lain = new Lain()
var saber = new Saber()
// 多态:当对不同的数据类型执行同一个操作时,如果表现出来的行为 (形态) 不一样,那么就是多态的体现.
function getFriends(Characters: Characters) {
  console.log(Characters.getFriends())
}
getFriends(lain)
getFriends(saber)
export { }

# JS 多态

  • JavaScript 有多态吗?

    • 维基百科对多态的定义:多态(英语:polymorphism)指为不同数据类型的实体提供统一的接口,或使用一个单一的符号来表示多个不同的类型。

    • 非常的抽象,个人的总结:不同的数据类型进行同一个操作,表现出不同的行为,就是多态的体现。

  • 从下面的定义来看,JavaScript 是一定存在多态的

// 多态:当对不同的数据类型执行同一个操作时,如果表现出来的行为 (形态) 不一样,那么就是多态的体现.
function Characters(foo) {
  console.log(foo.getFriends())
}
var lain = {
  name: "lain",
  getFriends: function() {
    return '樱岛麻衣'
  }
}
class Saber {
  getFriends() {
    return '小鸟游六花'
  }
}
var saber = new Saber()
Characters(lain) // 樱岛麻衣
Characters(saber) // 小鸟游六花
// 也是多态的体现
function sum(m, n) {
  return m + n
}
sum(22, 33)
sum("樱岛麻衣", "小鸟游六花")