JavaScript中class继承超乎你的想象《二》

本文深入探讨了JavaScript中class的高级用法,包括如何使用`Object.getPrototypeOf()`来判断类之间的继承关系,以及`super`关键字在构造函数与普通方法中的不同应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

接着上一篇,继续探讨javascript中class的另外用法:

Object.getPrototypeOf()

Object.getPrototypeOf方法可以用来从子类上获取父类。

Object.getPrototypeOf(Dog)===Aimals
//true
复制代码

因此我们可以使用这个方法判断一个类是否继承了另外一个类就很方便了。

super关键字的用法

super这个关键字既可以当做函数使用,也可以当做对象使用,在这两种情况下,它的作用完全不同。

1.第一种情况,super作为函数调用父类的构造函数,ES6要求,子类的构造函数必须执行一次super函数:

class A{
    
}
class B extends A{
    constructor(){
        super()
    }
}
复制代码

以上的代码,在子类的构造函数中必须使用super()去代表调用父类的构造函数,这个是必须的,否则javascript引擎会报错。

super虽然代表了父类A的构造函数,但是返回的是子类B的实例,即super内部的this指的是B,因此supe()在这里相当于A。prototype.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{
    constructor(){
        super()//使用方式正确
    }
    myMthod(){
        super()//报错,使用方式错误
    }
}
复制代码

2.第二种情况,super作为对象在普通的方法中指向父类的原型对象在静态方法中指向父类

class A{
    p(){
        retuen 2
    }
}
class B extends A{
    constructor(){
        super()
        console.log(super.p())//2
    }
}
let b=new B();
复制代码

在上面的代码中,子类B中的super.p(),就是将super当做一个对象来使用,这时,super在普通方法中指向了A.prototype,所以super.p()相当于A.prototype.p()。

划重点:由于super指向父类的原型对象,所以定义在父类实例上的方法或属性是无法通过super调用的。看下面的栗子:

class A{
    constructor(){
        this.p=2
    }
}
class B extends A{
    get m(){
        return super.p
    }
}

let b=new B()
b.m //undefined
复制代码

上面的栗子中,P是父类A实例中的方法,子类B通过super.p是引用不到的。如果这样定义在A的原型对象那么就没有问题:

class A{
    A.prototype.p=2
}
class B extends A{
    super()
    console.log(super.x)//2
}

let b=new B()
复制代码

上面的代码,属性P是定义在A.prototype上的,所以super.x可以取到它的值。ES6规定,通过super调用父类的方法,super会绑定子类的this。

class A{
    constructor(){
        this.x=1;
    }
    print(){
        console.log(this.x)
    }
}
class B extends A{
    constructor(){
        super()
        this.x=2
    }
    m(){
        super.print()
    }
}
let b=new B()
b.m()//2
复制代码

上面的super.print()虽然调用的是A.prototype.print(),但是A.prototype.print()会绑定B的this,导致输出的2,而不是1,也就是说执行的是super.print.call(this)。

由于绑定子类this,因此如果通过super对某个属性赋值,这时super就是this,赋值的属性就会变成子类实例的属性。

class A{
    constructor(){
        this.x=1
    }
}
class B extends A{
    constructor(){
        super()
        this.x=2
        super.x=3
        console.log(super.x)//undefind
        console.log(this.x)//3
    }
}
let b=new B()
复制代码

上面的代码super.x被赋值为3,等同于this.x=3.读取super.x时,相当于读取的是A.prototype.x,而父类的原型上并没有这个属性,所以undefined。

但是如果super作为对象再静态方法中使用,这个时候super将指向父类,而不是父类的原型对象。

class Parent(){
    static myMethods(msg){
        console.log('static',msg)
    }
    myMethods(msg){
        console.log('instance',msg)
    }
}
class Child extends Parent(){
    static myMethods(msg){
        super.myMethods(msg)
    }
    myMethods(msg){
        super.myMethods(msg)
    }
}
Child.myMethods(1)//static 1
let child=new Child()
child.myMethods(2)//instance 2
复制代码

上面的代码,super在静态方法中指向父类,在普通方法中指向父类的原型,所以使用super的时候一定要显示定义是作为函数还是对象使用,否则会报错。看如下:

class A{
}
class B extends A{
    constructor(){
        super()
        console.log(super)//报错
    }
}
复制代码

转载于:https://juejin.im/post/5b417999e51d45191556c6f6

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值