一、简介
- Class 可以通过extends关键字实现继承。
class Point{
}
class getColor extends Point{
}
- 上面代码定义了一个Color类,该类通过extends关键字,继承了Point类的所有属性和方法。
class Point{
constructor(x,y){
this.x=x;
this.y=y;
}
}
class getColor extends Point{
constructor(color,x,y){
super(x,y);
this.color=color;
}
toString(){
}
}
let color=new getColor('blue',12,22);
console.log(color);
- constructor方法,出现了super关键字,它在这里表示父类的构造函数,用来新建父类的this对象。
- 子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。
- 注意:(1)在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,基于父类实例,只有super方法才能调用父类实例。
- (2)父类的静态方法,也会被子类继承。
二、Object.getPrototypeOf()
- Object.getPrototypeOf方法可以用来从子类上获取父类。
console.log(Object.getPrototypeOf(getColor)===Point);
三、super 关键字
- super这个关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同。
- 第一种情况,super作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数。
class A{}
class B extends A{
constructor(){
super();
}
}
- 上面代码中,子类B的构造函数之中的super(),代表调用父类的构造函数。
- 作为函数时,super()只能用在子类的构造函数之中,用在其他地方就会报错。
- 注意,super虽然代表了父类A的构造函数,但是返回的是子类B的实例,即super内部的this指的是B的实例,因此super()在这里相当于A.prototype.constructor.call(this)。
- 第二种情况,super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
class A{
p(){
return 2;
}
}
class B extends A{
constructor(){
super();
console.log(super.p());
}
}
let b=new B();
- 上面代码中,子类B当中的super.p(),就是将super当作一个对象使用。这时,super在普通方法之中,指向A.prototype,所以super.p()就相当于A.prototype.p()。
- 这里需要注意,由于super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的。
- ES6 规定,在子类普通方法中通过super调用父类的方法时,方法内部的this指向当前的子类实例。‘
- 如果super作为对象,用在静态方法之中,这时super将指向父类,而不是父类的原型对象。
class Person{
name;
age;
sex;
constructor(name,age,sex){
this.name=name;
this.age=age;
this.sex=sex;
}
eat(){
}
sleep(){
return `${this.name} 开始睡觉`;
}
}
class student extends Person{
job;
constructor(name,age,sex,job){
super(name,age,sex);
this.job=job;
}
}
let stu=new student('小明',20,'男','学习');
console.log(stu);
console.log(stu.sleep());
- 另外,在子类的静态方法中通过super调用父类的方法时,方法内部的this指向当前的子类,而不是子类的实例。
class Point{
static pos;
constructor(x,y){
this.x=x;
this.y=y;
}
getPos(){
console.log(`坐标${this.x},${this.y}`);
Point.pos=[this.x,this.y];
}
}
class getColor extends Point{
constructor(color,x,y){
super(x,y);
this.color=color;
super.getPos();
}
static getValue(){
console.log(super.pos);
}
toString(){
}
}
let color=new getColor('blue',12,22);
console.log(color);
- ES6 规定,在子类普通方法中通过super调用父类的方法时,方法内部的this指向当前的子类实例。
四、类的 prototype 属性和__proto__属性
- 每一个对象都有__proto__属性,指向对应的构造函数的prototype属性。Class 作为构造函数的语法糖,同时有prototype属性和__proto__属性,因此同时存在两条继承链。
- (1)子类的__proto__属性,表示构造函数的继承,总是指向父类。
- (2)子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。
class A{
}
class B extends A{
}
console.log(B.prototype.__proto__);
console.log(B.__proto__);
五、实例的__ proto __ 属性
- 子类实例的__proto__属性的__proto__属性,指向父类实例的__proto__属性。也就是说,子类的原型的原型,是父类的原型。
}
class B extends A{
}
var p1=new A();
var p2=new B();
console.log(p2.__proto__ === p1.__proto__);
console.log(p2.__proto__.__proto__ === p1.__proto__);
- 上面代码中,B继承了A,导致前者原型的原型是后者的原型。
- 因此,通过子类实例的__proto__.__proto__属性,可以修改父类实例的行为。
p2.__proto__.__proto__.print=function(){
return 'hello world';
};
console.log(p1.print());