11. 新增class

2/21/2022 ES6

# 1. Class

class Person{
  constructor(name, id){
    this.name = name
    this.id = id
  }
  toString(){
    return `${this.name}: ${this.id}`
  }
}

# 1.1 class特性

  • 本质上是一个函数
  • 默认严格模式
  • 不存在提升行为
  • name属性是class关键字后面的类名
  • 内部[Symbol.iterator]表示Generator函数

在类中的方法都会定义在prototype上面

class Person{
  constructor(name, id){
    this.name = name;
    this.id = id;
  }
  toString(){
    return `${this.name}: ${this.id}`
  }
  toValue(){
    return this.id
  }
}
Object.getOwnPropertyNames(Person.prototype)  // ['constructor','toString','toValue']


// 等同于
Person.prototype = {
  constructor() {},
  toString() {},
  toValue() {},
}
Object.defineProperty(Person.prototype,'constructor',{
  value: Person,
  writable:false,
  enumerable: false, // 不可枚举喔~
  configurable: false
})

类的getter/setter

class Person {
  constructor(name, id) {
    this.name = name
    this.id = id
  }
  get _id() {
    return this.id + 1
  }
  set _id(value){
    this.id = value
  }
}
const p = new Person('jack', 107)
console.log(p._id)  // 108
console.log(p.id)  // 107
p._id = 100
console.log(p.id) // 100

this默认指向类实例,但是单独抽离出来会出现错误

class Person {
  constructor(name){
    this.name = name
  }
  sayName(){
    this.print(this.name)
  }
  print(text){
    console.log(text)
  }
}
const p = new Person('jack')
p.sayName() // jack
// 抽离出来
const{sayName} = p
sayName() // print is not defined,此时this指向的是window

改进

constructor(name){
  this.name = name
  this.sayName = this.sayName.bind(this)
}

# 2. Class静态方法

在一个方法前加上static关键字时,会定义方法在类上而非原型上,称为静态方法,即静态方法是供类调用的,非静态方法是供实例调用的

class Foo {
  static classMethod() {
    return 'hello'
  }
}

Foo.classMethod() // 'hello'

var foo = new Foo()
foo.classMethod() // TypeError: foo.classMethod is not a function

在static静态方法中,this指向的是类,而非实例,同时静态方法名可以与非静态方法名重名

class Foo {
  static bar() {
    this.baz()
  }
  static baz() {
    console.log('class')
  }
  baz() {
    console.log('instance.prototype')
  }
}
Foo.bar() // class

super关键字

  • static方法中指向父类
  • 非static方法中指向父类原型
class Foo {
  static fn() { // 该方法会定义在类上
    return 'static'
  }
  fn(){ // 该方法会定义在原型上
    return 'prototype'
  }
  fn2:(){ // 该方法会定义实例上
    return 'instance'
  }
}

class Bar extends Foo {
  static fn() {
    return super.fn()
  }
  fn(){
    return super.fn()  
  }
  fn2(){
    return super.fn2()
  }
}

Bar.fn() // "static"
let bar = new Bar()
bar.fn() // "prototype"
bar.fn2() // Uncaught TypeError: (intermediate value).fn1 is not a function
// 这是因为fn2定义在实例上而非原型上,而这里super指向的是父类原型

# 3. Class实例、原型、静态属性和方法定义

不多bb了,直接上代码

class Person{
  constructor(key1){ 
    this.key1 = key1 // instance
  }
  key2 = 1008 // instance
  get key3(){ return 'jack' } // prototype (getter/settter)
  static key4 = 1007 // constructor

  fn1 = function(){} // instance
  fn2: function(){} // instance
  fn3(){} // prototype
  static fn4(){} // constructor


}

# 4. 静态块

ES2022引入了静态块,允许在类的内部设置一个代码块,在通过类构造实例运行,用于对静态属性的初始化

class A {
  static x
  static y

  static {
    if(+new Date>10000) this.x = 1
    else this.x = 0 // 将A.x设置为0
  }
}

# 5. Class继承

# 5.1 extends关键字

在定义子类时,使用extends关键字指定其父类,在子类内部通过super指向父类,super()方法调用父类的constructor

class Father {
  constructor(name, id) {
    this.name = name
    this.id = id
  }
  fn1() { ... }
  static fn2(){ ... }
}
class Son extends Father {
  constructor(name, id, age) {
    super(name, id) // 调用父类的constructor
    this.age = age
  }
  fn1() {
    return super.fn1() + this.age // 调用父类原型的fn1
  }
  fn2(){
    return super.fn2() + this.age // 调用父类的fn2
  }
}

注意:子类必须在constructor()构造方法中调用super()方法来完成构造,从而获得与父类同样的实例属性和方法,再对其进行扩展,否则报错

若子类没有定义constructor方法会默认添加

class Son extends Father{}
//等同于
class Son extends Father{
  constructor(...args){
    super(...args)
  }
}

由于调用super方法,才能通过父类的构造函数创建子类实例,故this(指向子类实例)要在super后才有定义

class Point {
  constructor(name, id) {
    this.name = name
    this.id = id
  }
}

class ColorPoint extends Point {
  constructor(name, id, color) {
    this.color = color // ReferenceError
    super(name, id) // 通过父类构造函数创建子类实例
    this.color = color // 正确
  }
}

# 5.2 super关键字

在子类构造函数中必须要先调用super(),这时候会执行父类构造函数,并将this执行创建的子类实例

class A {
  constructor() {
    console.log(new.target.name)
  }
}
class B extends A {
  constructor() {
    super()
  }
}
new A() // A
new B() // B
    我不想谋生,
    我想生活。
    红莲华
    x
    loading...