1.简介
Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
上面代码定义了一个Student类,该类通过extends关键字,继承了Person类的所有属性和方法。但是由于没有部署任何代码,所以这两个类完全一样,等于复制了一个Person类。下面,我们在Student内部加上代码。
子类的constructor中出现了super关键字,它在这里表示父类的构造函数,用来新建父类的this对象。
子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。
不调用super方法就报错,没有this
ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this。
如果子类没有定义constructor方法,这个方法会被默认添加,代码如下。也就是说,不管有没有显式定义,任何一个子类都有constructor方法。
上面代码中,子类Student没有定义constructor,但还是继承了父类的属性name和age。
子类没有定义constructor自然也就没有调用super()方法,这时候子类中是不能用this关键字的,用了会报错,this is not defined
这是因为子类实例的构建,是基于对父类实例加工,只有super方法才能返回父类实例。
不先调用super()方法,子类中就不能用this
下面是生成子类实例的代码。
new 一个学生小明,8岁,三年级。
小明是学生的一个实例,true。 小明是学生
小明是人的一个实例,true。 小明是人
父类的静态方法,也会被子类继承。
2.Object.getPrototypeOf
Object.getPrototypeOf方法可以用来从子类上获取父类。
因此,可以使用这个方法判断,一个类是否继承了另一个类。
可以看到 Student类继承了Person类。
3.super关键字
super这个关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同。
第一种情况,super作为函数调用时,代表父类的构造函数。
ES6 要求,子类的构造函数必须执行一次super函数。
// 定义类
class Person{
}
class Student extends Person{
constructor(){
super();
}
}
上面代码中,子类B的构造函数之中的super(),代表调用父类的构造函数。这是必须的,否则 JavaScript 引擎会报错。
注意,super虽然代表了父类A的构造函数,但是返回的是子类B的实例,即super内部的this指的是B,因此super()在这里相当于A.prototype.constructor.call(this)。
class A {
constructor() {
console.log(new.target.name);
}
}
class B extends A {
constructor() {
super();
}
}
new A() // A
new B() // B
上面代码中,new.target指向当前正在执行的函数。可以看到,在super()执行时,它指向的是子类B的构造函数,而不是父类A的构造函数。也就是说,super()内部的this指向的是B。
作为函数时,super()只能用在子类的构造函数之中,用在其他地方就会报错。
class A {}
class B extends A {
toString() {
super(); // 报错
}
}
上面代码中,super()用在B类的toString方法之中,就会造成句法错误。
第二种情况,super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
class A {
toString() {
return 2;
}
}
class B extends A {
constructor() {
super();
console.log(super.toString()); // 2
}
}
let b = new B();
上面代码中,子类B当中的super.toString(),就是将super当作一个对象使用。这时,super在普通方法之中,指向A.prototype,所以super.toString()就相当于A.prototype.toString()
这里需要注意,由于super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的。
上面代码中,age是父类A实例的属性,super.age就引用不到它。