JavaScript实现继承

本文详细介绍了JavaScript中的七种继承方式:原型链继承、构造函数继承、组合继承、原型式继承、寄生式继承、寄生组合式继承以及ES6的类继承。每种方式都有其特点和缺点,例如原型链继承会共享属性,构造函数继承无法继承原型方法,组合继承解决了前两者的问题但存在冗余,而寄生组合式继承是最优解。此外,还提到了混入式继承用于多重继承场景,以及ES6的`extends`关键字简化了类的继承。
摘要由CSDN通过智能技术生成

JavaScript的继承方式

1、原型链继承

核心:将父类的实例对象作为子类的原型对象,及继承了父类原型的方法也继承了父类实例的属性。

          // 父类
        function SuperType() {
            this.property = true
            this.colors = ['red','blue']
        }
        // 父类原型的方法
        SuperType.prototype.getSuperValue = function () {
            return this.property
        }
        // 子类
        function SubType() {
            this.subproperty = false
        }
        // 子类的原型对象为 父类的实例对象
        SubType.prototype = new SuperType()

        // 给子类原型添加方法 一定要注意顺序
        SubType.prototype.getSubValue = function () {
            return this.subproperty
        }

        let sub1 = new SubType()
        let sub2 = new SubType()
        // 修改父类的引用类型的数据 ,会影响其他实例对象
        sub1.property = false
        sub1.colors.push('yellow')
        console.log(sub2);
        console.log(sub1.getSuperValue());
        console.log(sub2.getSuperValue());


缺点

  • 父类的所有引用类型的属性会被所有子类共享,一个子类修改,所有子类都发生改变。
  • 不能传递参数

2、构造函数继承

核心:在子类构造函数中调用父类构造函数,可以在子类构造函数中使用call()apply()方法,改变this指向。

        // 父类
        function SuperType() {
            this.property = true
        }
        // 父类原型的方法
        SuperType.prototype.getSuperValue = function () {
            return this.property
        }
        // 子类
        function SubType() {
            // 通过改变this指向从而继承父类构造函数的属性
            // 绑定在实例身上
            SuperType.call(this)
        }

        let sub1 = new SubType()
        let sub2 = new SubType()
        sub1.property = false
        console.log(sub1.property);  //false
        console.log(sub2.property); //true
        // console.log(sub1.getSuperValue());//sub1.getSuperValue is not a function

由于执行SuperType.call(this),所以创建子类实例时,会调用SuperType构造函数,于是SubType的实例都会将SuperType中的属性复制一份。

缺点

  • 无法继承父类的原型对象的属性和方法

3、组合继承

核心: 使用原型链继承父类原型上的方法和属性,使用构造函数继承父类实例的方法和属性。

        // 父类
        function SuperType(name) {
            this.name = name
        }
        SuperType.prototype.sayName = function () {
            return this.name
        }
        // 子类
        function SubType(name, age) {
      		//第二次调用
            // 继承父类的实例属性 name
            SuperType.call(this, name)
            this.age = age
        }

        // 继承父类原型上的方法和属性
        SubType.prototype = new SuperType()
        // 因为改变了子类的原型对象
        //第一次调用
        // 所以重写SubType.prototype的constructor属性,指向自己的构造函数SubType
        SubType.prototype.constructor = SubType
        // 给子类原型添加方法 注意这里的位置,要在继承之后
        SubType.prototype.sayAge = function () {
            return this.age
        }

        let sub1 = new SubType('hwm1', 21)
        let sub2 = new SubType('hwm2', 22)
        console.log(sub1.sayName()); //hwm1
        console.log(sub2.sayName()); //hwm2
        console.log(sub1.sayAge());  //21
        console.log(sub2.sayAge());  //22


SuperType()被调用两次

  • 第一次会给SubType.prototype写入SuperType实例的属性name
  • 第二次会给SubType的实例对象,写入SuperType实例的属性name

实例对象上的属性会屏蔽其原型对象上的同名属性。

缺点

  • 在创建子类实例对象时,会存在两份相同的属性和方法。

4、原型式继承

核心:利用一个空构造函数作为中介,将需要继承的对象浅复制给这个空构造函数的原型,通过构造函数创建一个实例。

        let obj = {
            name: 'hwm',
            colors: ['red', 'yellow', 'blue'],
            sayName(){
                return this.name
            }
        }
        // 将obj浅复制
        function object(obj) {
            // 空的构造函数
            function F() { }
            // 继承了obj对象的属性和方法
            F.prototype = obj
            return new F()
        }

        let sub1 = object(obj)
        let sub2 = object(obj)
        sub1.name = 'sub1'
        sub2.name = 'sub2'
        sub1.colors.push('pink')
        console.log(sub2.colors); //['red', 'yellow', 'blue', 'pink']
        console.log(sub1.sayName()); //sub1
        console.log(sub2.sayName());//sub2

可以直接使用ES5的Object.create()方法。创建一个原型为obj的对象。

        let sub3 = Object.create(obj)
        let sub4 = Object.create(obj)
        //直接在对象上添加新属性,而不是修改父类的属性
        sub3.name = 'sub3'
        sub4.name = 'sub4'
        sub3.colors.push('pink')
        console.log(sub4.colors); //['red', 'yellow', 'blue', 'pink']
        console.log(sub3.sayName());
        console.log(sub4.sayName());

缺点

  • 因为是浅复制,继承的属性如果是引用类型,存在篡改的可能。
  • 无法传递参数

5、寄生式继承

核心:在原型式继承的基础上,给创建的对象添加属性和方法。

        let obj = {
            name: 'hwm',
            colors: ['red', 'yellow', 'blue']
        }
        
        function SubType(obj) {
            // 浅复制对象
            let clone = Object.create(obj)
            clone.sayName = function () {
                return this.name
            }
            return clone
        }

        let sub1 = SubType(obj)
        let sub2 = SubType(obj)
        sub1.name = 'sub1'
        sub2.name = 'sub2'
        console.log(sub1.sayName());
        console.log(sub2.sayName());
        sub1.colors.push('pink')
        console.log(sub2.colors); //['red', 'yellow', 'blue', 'pink']
        console.log(sub1);
        console.log(sub2);

函数SubType的作用就是为构造函数新增属性和方法。

缺点

  • 因为是浅复制,继承的属性如果是引用类型,存在篡改的可能。
  • 无法传递参数

6、寄生组合式继承

结合构造函数继承寄生式继承这是最成熟的方法,也是现在库实现的方法

        // 父类
        function SuperType(name) {
            this.name = name
            this.colors = ['red', 'yellow']
        }
        SuperType.prototype.sayName = function () {
            return this.name
        }
        // 子类
        function SubType(name, age) {
            // 继承父类实例属性
            SuperType.call(this, name)
            this.age = age
        }
        // 继承父类原型上的属性
        function inheritPrototype(subType, superType) {
            // 浅复制父类原型对象
            let prototype = Object.create(superType.prototype)
            // 添加复制对象的constructor 属性指向子类
            prototype.constructor = subType
            subType.prototype = prototype
        }
        inheritPrototype(SubType, SuperType)

        // 给子类原型添加方法
        SubType.prototype.sayAge = function () {
            return this.age
        }

        let sub1 = new SubType('sub1', 21)
        let sub2 = new SubType('sub2', 22)
        console.log(sub1);
        sub1.colors.push('1')
        sub2.colors.push('2')
        console.log(sub1.colors);//['red', 'yellow', '1']
        console.log(sub2.colors);//['red', 'yellow', '2']

这个方式比组合式继承高效,因为SuperType函数只调用了一次。此方法没有使用原型链继承的方式继承父类的原型,而是通过创建一个原型为父类原型的对象,并将该对象的constructor指向子类,并将该对象赋值给子类原型。

7、混入方式继承多个对象

核心:在寄生组合式继承的基础上,通过Object.assign方式将其他父类的原型对象拷贝到子类原型上

              // 父类
        function SuperType(name) {
            this.name = name
            this.colors = ['red', 'yellow']
        }
        SuperType.prototype.sayName = function(){
            return this.name
        }
        // 父类
        function OtherSuper(age) {
            this.age = age
        }
        OtherSuper.prototype.sayAge = function(){
            return this.age
        }
        // 子类
        function SubType(name, age) {
            // 继承父类实例属性
            SuperType.call(this, name)
            OtherSuper.call(this, age)
        }
        // 子类的原型 浅复制父原型的属性和方法
        SubType.prototype = Object.create(SuperType.prototype)
        // 混入其他父类 将OtherSuper.prototype的方法和属性 混入SubType.prototype
        Object.assign(SubType.prototype, OtherSuper.prototype)
        // 重新指定constructor
        SubType.prototype.constructor = SubType
        // 添加原型方法
        SubType.prototype.sayHi = function () {
            console.log('hi');
        }

        let sub1 = new SubType('sub1', 21)
        let sub2 = new SubType('sub2', 22)
        sub1.colors.push('1')
        sub2.colors.push('2')
        console.log(sub1.colors);
        console.log(sub2.colors);
        console.log(sub1);

8、ES6类继承extends

        // 父类
        class SuperType {

            colors = ['red', 'yellow']
            constructor(name) {
                this.name = name
            }
        }
        SuperType.prototype.sayName = function () {
            return this.name
        }
        // 子类继承
        class SubType extends SuperType {
            constructor(name, age) {
                // 继承父类的属性
                super(name)
                this.age = age
            }
        }
        SubType.prototype.sayAge = function () {
            return this.age
        }

        let sub1 = new SubType('sub1', 21)
        let sub2 = new SubType('sub2', 22)
        console.log(sub1.sayName());
        sub1.colors.push('1')
        console.log(sub2.colors);//['red', 'yellow']
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值