继承模式——JS

本文详细探讨了JavaScript中的继承模式,包括原型链继承、借用构造函数继承和组合继承。通过实例解析了各种继承方式的问题及解决方案,如原型链中constructor属性的修正。最后,介绍了组合继承作为常用的继承方式,它结合了原型链和构造函数的优点,避免了各自的缺点。

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

继承模式

先搭建好继承框架:

        // 父类型
        function Supper (){
           this.supProp = 'Supper property'
        }
        Supper.prototype.showSupperPop = function (){
           console.log(this.supProp)
        }

        // 子类型
        function Sub (){
           this.subProp = 'Sub property'
        }
        Sub.prototype.showSubPop = function (){
           console.log(this.subProp)
        }

        var sub = new Sub()
        sub.showSupperPop()//想要实现调用,当然现在这样写是错误的

原型链继承

首先思考如何能够看到 Supper的showSupperPop方法

  • 利用原型链
    如果子类型的原型对象成为父类型的一个实例对象,子类型就可以通过原型链看到父类型的属性和方法了,即实现了继承。
    对应代码:Sub.prototype = new Supper()

解释:
上面代码的原型链:
在这里插入图片描述
如果想要sub实例可以访问Supper原型对象的showSupperPop(),就需要有一个箭头从 sub原型对象的__proto__指向 Supper的原型对象。即,使子类型的原型对象成为父类型的一个实例对象。
可能会问为什么不直接 从 sub实例对象的 __proto__指向 Supper的原型对象,如果这样的话,子类型的属性和方法就访问不到了。
注意,一个对象的 proto 只能存一个值。

(这里先这样理解,实际上真正实现继承不单单只是将sub原型对象的__proto__的指向改变了,可以看下面实际操作一步步画出的原型链进行理解。)

实现:
将子类型的原型对象定义为父类型的实例对象

        // 父类型
        function Supper (){
           this.supProp = 'Supper property'
        }
        Supper.prototype.showSupperPop = function (){
           console.log(this.supProp)
        }

        // 子类型
        function Sub (){
           this.subProp = 'Sub property'
        }
        // 子类型的原型成为夫类型的实例对象
        Sub.prototype = new Supper()
        Sub.prototype.showSubPop = function (){
           console.log(this.subProp)
        }

        var sub = new Sub()
        sub.showSupperPop()
        sub.showSubPop()
        console.log(sub.toString()) 

输出:
在这里插入图片描述
此时对应的原型链:
在这里插入图片描述

问题

问题产生

constructor属性返回对象的构造函数。
那么上面的代码如果执行console.log(sub.constructor),会是什么结果呢

        // 父类型
        function Supper (){
           this.supProp = 'Supper property'
        }
        Supper.prototype.showSupperPop = function (){
           console.log(this.supProp)
        }
        // 子类型
        function Sub (){
           this.subProp = 'Sub property'
        }

        // 子类型的原型成为夫类型的实例对象
        Sub.prototype = new Supper()
        Sub.prototype.showSubPop = function (){
           console.log(this.subProp)
        }

        var sub = new Sub()
      //    constructor属性返回对象的构造函数
        console.log(sub.constructor)

输出:
在这里插入图片描述
输出的是Supper而不是我们预想的Sub,这是什么原因呢?
还是看刚才的原型链:sub实例对象没有constructor属性,就会像上找找到Supper的实例对象,发现也没有该属性,继续上找,找到Supper的原型对象发现有该属性,并且指向Supper的函数对象,所以输出就是Supper的函数对象。
在这里插入图片描述

问题解决

我们一般让对象的constructor指向自己的原型对象,所以做如下修改:
// 让子类型原型对象的constructor属性指向Sub
Sub.prototype.constructor = Sub
代码:

        // 父类型
        function Supper (){
           this.supProp = 'Supper property'
        }
        Supper.prototype.showSupperPop = function (){
           console.log(this.supProp)
        }
        // 子类型
        function Sub (){
           this.subProp = 'Sub property'
        }

        // 子类型的原型成为夫类型的实例对象
        Sub.prototype = new Supper()
      //   让子类型原型对象的constructor属性指向Sub
        Sub.prototype.constructor = Sub
        Sub.prototype.showSubPop = function (){
           console.log(this.subProp)
        }

        var sub = new Sub()

      //    constructor属性返回对象的构造函数
        console.log(sub.constructor)

输出:
在这里插入图片描述

对应的原型链:
在这里插入图片描述

借用构造函数继承——不是真正的继承

方法:
1.定义父类型构造函数
2.定义子类型构造函数
3.在子类型构造函数中调用父类型构造,利用函数的call方法
call方法的作用就是短暂的调用一个不属于自己的函数。
eg:

        function Person (name ,age){
           this.name = name
           this.age =age
        }
        function Student (name,age,price){
           Person.call(this,name,age)   
        /*
        含义相当于 this.Person(name,age)
        即:
           this.name = name
           this.age =age
        (注意this.Person(name,age)这句话不能执行,this没有Person属性,这样写只是为了方便理解)
        */
            this.price = price
        }
        var s =new Student('Tom',12,10000)
        console.log(s.name,s.age,s.price)

输出:
在这里插入图片描述

组合继承

组合继承就是将原型链继承和借用构造函数继承结合起来。

  • 利用原型链实现对父类型对象的方法继承
  • 利用call()借用父类型构造函数初始化相同属性
    eg:
        function Person (name ,age){
           this.name = name
           this.age =age
        }
        Person.prototype.setName=function(name){
            this.name = name
        }


        function Student (name,age,price){
            // 借用父类型构造函数初始化相同属性
            //使用call,下面Student.prototype = new Person()就无需传参了
           Person.call(this,name,age)   
            this.price = price
        }
        // 继承原型链,继承父类型对象的方法继承
        //因为上面使用Person.call(this,name,age),这里就无需传参了  
        Student.prototype = new Person()
        // 修正constructor属性
        Student.prototype.constructor =Student

        Student.prototype.setPrice=function(price){
            this.setPrice = price
        }
        var s = new Student('Tom',24,10000)
        s.setPrice(16000)
        s.setName('Bob')
        console.log(s.name,s.age,s.price)

输出:
在这里插入图片描述

我们通常使用的是组合继承

  • 为什么不使用原型链继承?
    因为夫类型的构造函数一般是要传递参数的,而我们使用 Sub.prototype = new Supper()只是为了建立原型链的关系,一般不需要传参,就出现了矛盾。

如果执意不传参,就会出现如下错误:

        function Person (name ,age){
           this.name = name
           this.age =age
        }
        Person.prototype.setName=function(name){
            this.name = name
        }


        function Student (name,age,price){
            // 借用父类型构造函数初始化相同属性
        //    Person.call(this,name,age)  
            this.name =name
            this.age =age
            this.price = price
        }
        // 继承原型链,继承父类型对象的方法继承
        Student.prototype = new Person()
        // 修正constructor属性
        Student.prototype.constructor =Student

        Student.prototype.setPrice=function(price){
            this.setPrice = price
        }
        var s = new Student('Tom',24,10000)
        s.setName('Bob')
        s.setPrice(16000)
        console.log(s.name,s.age,s.price)

输出:
在这里插入图片描述

  • 为什么不使用借用构造函数继承?
    因为他只是表面上的简单引用,原型链上的方法属性不会继承。

所以使用两种方式相结合的方法实现组合继承:
在子类型的构造函数中使用 父构造函数.call(this,参数),下面再将子类型的原型修改为父类型的实例的时候就无需传参了 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值