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...