Class
- 内部存在constructor成为构造函数,方法内部的this指向实例,默认的构造函数为一个空函数,每次new的时候都会执行该函数。
- 类的类型是函数,类本身就指向构造函数。
- 类内部所有定义的方法都是不可枚举的。
- 类必须使用new实例化,否则会报错。
- 类的属性和方法除非显式定义在本身(this)上,否则全部定义在原型(class)上。
- ES2022 类的属性除了定义在constructor中的this上,还可以直接定义在类的最顶层,这种定义的属性是定义在实例对象自身的属性,而不是定义在实例对象原型上的方法。
- 类内部定义的属性也可以定义存值(set)和取值(get)函数,对属性的访问和赋值进行自定义。
- 类的属性名可以采用表达式的方式。
- 类也可以使用表达式的形式定义
const MyClass = class Me{ ... },此时类的名字是Me但是这个名字只能在类的内部调用,若内部没有用到,则可以省略,直接定义const MyClass = class { ... }。外部只能使用MyClass。这种方式定义类还可以定义立即执行Classconst person = { ... }(params)。 - 在类上定义的属性和方法都会被实例继承,但是在属性或者方法前加上static,表示属于静态方法或属性,只能通过来来调用,实例不会继承这些。如果静态方法包含this,这个this指向的是类本身,而不是实例对象。
- 父类的静态方法可以被子类继承,静态方法可以在super上被调用。静态属性相同。
- 类内部的私有属性可以在属性或方法前加 # 表示,外部访问会报错,即使访问不存在的私有属性也会报错。只有内部或者实例可以使用私有属性或方法。
- 静态块在类生成的时候运行一次,主要是用作对静态属性的初始化,静态块的内部可以使用类名或者this,指向当前类,另一个作用是将私有属性与类的外部代码分享。
let getX; export class C { #x = 1; static { getX = obj => obj.#x; } } console.log(getX(new C())); // 1
###注意点
- 类不存在变量提升
- name属性总是返回紧跟在class关键字后面的类名
- this指向问题:类的方法内部的this默认指向类的实例,但是如果将该方法单提出来使用可能会报错,有两种结婚方法:
class Logger {
constructor() {
this.printName = this.printName.bind(this);
}
// ...
}
class Obj {
constructor() {
this.getThis = () => this;
}
}
const myObj = new Obj();
myObj.getThis() === myObj // true
还有一种就是proxy代理,在获取时自动绑定this,实现比较麻烦。
- new.target 返回new命令作用的类的构造函数,如果构造函数不是通过new调用会返回undefined,该属性可以用来确定构造函数是怎么调用的。
- class内部调用new.target,会返回当前的Class。
- 子类继承父类时,new.target会返回子类。利用这一点可以写出不能独立使用,只能被继承的类。
类的继承
- 子类必须在构造函数内调用super方法,用于新建一个父类的实例对象。原因是子类自己的this对象必须先通过父类的构造函数完成塑造,得到与父类同样的实例方法和属性,再添加子类自己的属性或方法,如果不调用子类就找不到自己的this对象。
- 新建子类实例时,父类的构造函数必定会先运行一次。
- 在子类的构造函数中,只有调用的super之后,才能使用this关键字,否则会报错。因为子类实例的构建,必须先完成父类的继承。
- 除了私有属性,父类的所有属性和方法都会被子类继承,包括静态方法。
- 子类无法继承父类的私有属性,私有属性只能在定义它的class中使用。
- 如果父类定义了私有属性的读写方法,子类可以通过这些方法,读写私有属性。
Object.getPrototypeOf()方法可以用来从子类上获取父类。可以用于判断一个类是否继承了另一个类。
super
- 子类构造函数中的super()表示调用父类的构造函数,且是必须调用的,但是返回的是子类的实例。super()的调用只能是在子类的构造函数内部,在其他地方调用会报错。
class A { constructor() { console.log(new.target.name); } } class B extends A { constructor() { super(); } } new A() // A new B() // B - super作为对象时,在普通方法中调用,指向父类的原型对象,在静态方法中指向父类。
- 在普通方法中调用时指向父类的原型对象,所以父类实例上的方法或者属性是无法通过super访问的。如果定义在父类的原型上则可以访问。
- 在子类普通方法中的通过super调用父类的方法是,方法内部的this指向是当前子类的实例。
- 由于
this指向子类实例,所以如果通过super对某个属性赋值,这时super就是this,赋值的属性会变成子类实例的属性。 - 在子类的静态方法中通过
super调用父类的方法时,方法内部的this指向当前的子类,而不是子类的实例。class A { constructor() { this.x = 1; } static print() { console.log(this.x); } } class B extends A { constructor() { super(); this.x = 2; } static m() { super.print(); } } B.x = 3; B.m() // 3 - 由于对象总是继承其他对象的,所以可以在任意一个对象中,使用
super关键字。var obj = { toString() { return "MyObject: " + super.toString(); } }; obj.toString(); // MyObject: [object Object]
proto prototype
- 作为一个对象,子类(
B)的原型(__proto__属性)是父类(A);作为一个构造函数,子类(B)的原型对象(prototype属性)是父类的原型对象(prototype属性)的实例 - class可以实现源生构造函数的继承。