类 | 深刻理解super

super这个关键词既可以当对象使用,也可以当函数使用

super作为函数使用

super作为函数使用时,只能在子类的构造函数中【目的是为了调用父类的构造函数】 => super表示父类构造函数,但是this指向当前子类的构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Father {
constructor(name, age) {
this.name = name;
this.age = age;
console.log(new.target.name); // new.target 指向当前正在执行的函数
}
}

class Son extends Father {
constructor(name, age) {
super(name, age);
}
}

new Father(); // Father
new Son(); // Son

在子类的constructor中必须调用super()方法,因为子类没有自己的this对象,而是要继承父类的this对象,但是此时this指向的是子类的构造函数,此时super就表示了父类的构造函数,super()此时相当于Father.prototype.constructor.call(this, props)

super作为对象使用

super作为普通方法使用

(1)super指向的是父类的原型对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Father {
static c() {
return 1;
};
c() {
return 11;
}
constructor() {};
}

class Son extends Father {
constructor() {
super();
console.log(super.c()); // 11
}
}

new Son();

在上面的代码中,子类中的super.c(),就是把super当作一个对象使用,对象里面有c方法,因此super在普通函数中指向,Father.prototype,也就是说super.c()相当于Father.prototype.c()

(2)通过super调用父类方法时,super内部的this指向子类的实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Father {
x = 7;
fun() {
this.x = 77;
};
print() {
console.log(this.x);
};
}

class Son extends Father {
x = 777;
fn() {
super.fun();
this.x = 777;
super.print(); // 777
}
}

new Son().fn();

/*
如果没有 this.x = 7777 则返回77
如果没有 super.fun() 则返回 777
如果没有 this.x = 777 则返回 7
*/

此时的super可以替换this,与this.print()等价

(3)当通过super为子类属性赋值时,super就是this
1
2
3
4
5
6
7
8
9
10
11
class Father {}

Father.prototype.c = 77;

class Son extends Father {
fn() {
super.c = 7; // 为子类赋值的情况下,super为this
console.log(super.c); // 7 super获取的是父类的原型
console.log(this.c); // 7
}
}

赋值的情况下super就是this

super作为静态方法使用

(1)super指向的是父类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 例1
class Father {}

Father.prototype.c = function() {
return 11;
}

class Son extends Father {
fn() {
super.c = function() { // 为子类赋值的情况下super为this
return 1
};
console.log(super.c()); // 11 super为父类的prototype
console.log(this.c()); // 1 this指向子类构造函数
}
}

new Son().fn();


// 例2
class Father {
static c() { // 静态方法
console.log(2);
}

c() { // 原型对象中的方法
console.log(22);
}
}

class Son extends Father {
static s() {
super.c(); // super指向父类 调用的是静态方法
}

s() {
super.c(); // super指向父类原型对象
}
}

Son.son(); // 2 调用的是Son的静态方法,指向父类静态方法
new Son().c(); // 22 调用的是Son的原型方法,指向父类原型方法
(2)在子类的静态方法中通过super调用父类方法是,super内部的this指向子类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Father {
static x = 3;
constructor() {
this.x = 2;
};
static print() {
console.log(this.x);
}
}

class Son extends Father {
constructor() {
super(); // 构造函数内调用
this.x = 4; // this指向子类构造函数
}

static fn() {
this.x = 5;
// 如果没有this.x = 5,则 Son.x 打印为1
// 如果既没有this.x = 5,也没有Bar.x = 1,则打印3
// 如果既没有this.x = 5,也没有Bar.x = 1 和 x =3,则打印undefined,不会输出构造函数中的2
super.print(); // super
}
}

Son.x = 1;
Son.fn();

例题

注意看注释~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Father {
name = 'Father';
age = 18;

getName() {
return this.name;
}

getAge() {
return this.age;
}
}

class Son extends Father {
constructor() {
super(); // 这里调用super()是为了调用父类的构造函数
// console.log(this); // this指向的是子类的构造函数 this 为 Son {name:'Father', age: 18}
}

getAge() {
// console.log(this.getAge)
return this.getAge() + 10;
}
}

console.log(new Son().getName()); // Father
// console.log(new Son().getAge()); // 报错 因为 new Son().getAge()调用的是子类构造函数的getAge函数,它一直在调用自身
console.log(new Son().getAge.call({age:2})); // 报错 这里this是{age:2}, 此时this.getAge为undefined

参考

理解 es6 class 中 constructor 方法 和 super 的作用

前端面试之关于super的全方位解读