JavaScript面向对象系列之继承的多种方式

js本身是基于面向对象开发的编程语言。类有以下特性:封装、继承、多态。那这几种特性在js中是如何表现的呢?

封装:js中类也是一个函数,把实现一个功能的代码进行封装,以此实现低耦合高内聚

多态:包括重载和重写

  • 重载:相同的方法,由于参数或者返回值不同,具备不同的功能。js中不具备严格意义上的重载,因为在其它语言中,不同的传参可以分为不同的函数。如果想要js实现重载,就只能是在同一个方法内,根据传参不同实现不同的功能
  • 重写:子类重写父类上的方法(伴随着继承)

继承:子类继承父类上的方法

继承

在js语言中,他的继承和其他语言不太一样

继承的目的:让子类的实例同事也具备父类中私有的属性和公共方法

function Parent(){
        this.x = 100
    }
    Parent.prototype.getX = function getX(){
        return this.x
    }
    function Child(){
        this.y = 200
    }
    Child.prototype.getY = function getY(){
        return this.y
    }
    let c1 = new Child
    console.log(c1)
复制代码

想让 c1 也可以有 xgetX

继承的方法

原型继承

让子类的原型等于父类的实例即可

因为子类的实例,能够用子类私有的和原形公有的,同样,父类的实例也能够用父类私有的和原形公有的。

function Parent(){
        this.x = 100
    }
    Parent.prototype.getX = function getX(){
        return this.x
    }
    function Child(){
        this.y = 200
    }
    Child.prototype = new Parent//加一句话即可
    Child.prototype.constructor = Child//3指定construct
    
    Child.prototype.getY = function getY(){
        return this.y
    }
    let c1 = new Child
    console.log(c1)
复制代码

原型链的指向如下:

image。png

原型继承的特点

  1. 把父类中私有的和公有的属性方法,继承完后,最终都变为子类实例公有的
  2. 和其他语言不通,原型继承并不会吧父类的属性方法拷贝给子类,而是让子类实例基于 __proto__ 原型链找到自己自定义的属性和方法(”指向/查找”方式的继承,而不是拷贝式继承)

重写:

c1.__proto__.xxx=xxx 修改子类原型(原有父类的一个实例)中的内容,内容被修改后,对子类的其他实例有影响,但是对父类的实例不会有影响

c1.__proto__.__proto__.xxx=xxx 直接修改的是父类的原型,这样不仅会影响其他父类的实例,也影响其他子类的实例

call 继承

在子类构造函数中,把父类当做普通方法执行,并且将父类中的this指向子类。

function Parent(){
        this.x = 100
    }
    Parent.prototype.getX = function getX(){
        return this.x
    }
    function Child(){
        //在子类构造函数中,把父类当做普通方法执行(没有父类实例,父类原型上的那些东西也就和他没关系了)
        //this.x = 100相当于强制给c1这个实例设置一个私有的属性x,属性值100,相当于让子类的实例继承了父类的私有属性,并且也变为了子类私有的属性
        Parent.call(this)
        this.y = 200
    }
    Child.prototype.getY = function getY(){
        return this.y
    }
    let c1 = new Child
    console.log(c1.getX())
    console.log(c1)
复制代码

这种方法无法继承父类的私有属性。

上面两种继承都有缺点,我们想要的是父类私有变成子类私有,父类共有也变为子类私有

寄生组合继承

如果想让子类也继承父类的公有属性,只要保证 Child.prototype.__proto__ 指向 Parent.prototype 即可

分为两步

  1. Parent.call(this) 继承私有属性和方法
  2. Child.prototype.__proto__ = Parent.prototype 继承公有属性和方法

其实就是call继承+另类原型继承的组合

 function Parent(){
    this.x = 100
}
Parent.prototype.getX = function getX(){
    return this.x
}
function Child(){
    Parent.call(this)//1.
    this.y = 200
}
Child.prototype.__proto__ = Parent.prototype//2.
Child.prototype.constructor = Child//3

Child.prototype.getY = function getY(){
    return this.y
}
let c1 = new Child
console.log(c1.getX())
console.log(c1)
复制代码

image。png

如果浏览器不支持 __proto__ (例如ie),那么我们可以借助 Object.create

Object。create

Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的 __proto__

即创建一个新对象,并且将 __protot__ 指向传入的对象

所以将 Child.prototype.__proto__ = Parent.prototype 修改为 Child.prototype = Object.create(Parent.prototype)

最后再指定 constructor 即可

function Parent(){
    this.x = 100
}
Parent.prototype.getX = function getX(){
    return this.x
}
function Child(){
    Parent.call(this)//1.
    this.y = 200
}
Child.prototype.__proto__ = Parent.prototype//2
Child.prototype.constructor = Child//3

Child.prototype.getY = function getY(){
    return this.y
}
let c1 = new Child
console.log(c1.getX())
console.log(c1)
复制代码

image。png

ES6继承

使用 extends 关键字。
注意:继承后一定要在 constructor 第一行加上 super()

class Parent{
        constructor() {
            this.x = 100
        }
        getX(){//相当于在原型上写方法
            return this.x
        }
    }
    //继承:extends Parent
    //注意:继承后一定要在constructor 第一行加上super()
    class Child extends Parent{
        constructor() {
            super()
            this.y = 200
        }
        getY(){
            return this.y
        }
    }
    let c1 = new Child
    console.log(c1.getX())
    console.log(c1)
复制代码

image。png

不加 super() 会报错

'Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor'

执行 super() 类似于我们之前的call继承,如果执行 super(100,200) ,就相当于把前面的 Parent 构造函数执行,并传入 100,200

最终实现效果类似于寄生组合继承

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享