js 四种继承方式

49 篇文章 0 订阅
34 篇文章 0 订阅

继承
说到继承,最容易想到的是ES6的extends,当然如果只回答这个肯定不合格,我们要从函数和原型链的角度上实现继承,下面我们一步步地、递进地实现一个合格的继承

实现一个方法可以从而实现对父类的属性和方法的继承,解决代码冗余重复的问题

一. 原型链继承
原型链继承的原理很简单,

直接让子类的原型对象指向父类实例,

Child.prototype=new Parent()
1
当子类实例找不到对应的属性和方法时,就会往它的原型对象,也就是父类实例上找,

从而实现对父类的属性和方法的继承

原型继承的缺点:

1.由于所有Child实例原型都指向同一个Parent实例, 因此对某个Child实例的父类引用类型变量修改会影响所有的Child实例

2.在创建子类实例时无法向父类构造传参, 即没有实现super()的功能

二. 构造函数继承
构造函数继承,即在子类的构造函数中执行父类的构造函数,并为其绑定子类的this,

让父类的构造函数把成员属性和方法都挂到子类的this上去;

在Child的构造函数中执行
Parent.apply(this, arguments);
1
2
这样既能避免实例之间共享一个原型实例,又能向父类构造方法传参;

js继承的方式继承不到父类原型上的属性和方法

构造函数继承的缺点:

1.继承不到父类原型上的属性和方法

三. 组合式继承
既然原型链继承和构造函数继承各有互补的优缺点, 那么我们为什么不组合起来使用呢, 所以就有了综合二者的组合式继承

Child.prototype=new Parent()
Child.prototype.constructor=Child  //相当于在Child的构造函数中给Parent绑定this
1
2
组合式继承的缺点:

1.每次创建子类实例都执行了两次构造函数(Parent.call()和new Parent()),虽然这并不影响对父类的继承,但子类创建实例时,原型中会存在两份相同的属性和方法,这并不优雅

四. 寄生式组合继承
为了解决组合式继承中构造函数被执行两次的问题,

我们将指向父类实例改为指向父类原型, 减去一次构造函数的执行

到这里我们就完成了ES5环境下的继承的实现,这种继承方式称为寄生组合式继承。

Function.prototype.extend = function (supClass) {
  // 创建一个中间替代类,防止多次执行父类(超类)的构造函数
       function F() { }
       // 将父类的原型赋值给这个中间替代类
       F.prototype = supClass.prototype;
       // 将原子类的原型保存
       var proto = subClass.prototype;
       // 将子类的原型设置为中间替代类的实例对象
       subClass.prototype = new F();
       // 将原子类的原型复制到子类原型上,合并超类原型和子类原型的属性方法
       // Object.assign(subClass.prototype,proto);
       var names = Object.getOwnPropertyNames(proto);
       for (var i = 0; i < names.length; i++) {
           var desc = Object.getOwnPropertyDescriptor(proto, names[i]);
           Object.defineProperty(subClass.prototype, names[i], desc);
       }
       // 设置子类的构造函数时自身的构造函数,以防止因为设置原型而覆盖构造函数
       subClass.prototype.constructor = subClass;
       // 给子类的原型中添加一个属性,可以快捷的调用到父类的原型方法
       subClass.prototype.superClass = supClass.prototype;
       // 如果父类的原型构造函数指向的不是父类构造函数,重新指向
       if (supClass.prototype.constructor !== supClass) {
           supClass.prototype.constructor = supClass;
       }
   }

   function Ball(_a) {
       this.superClass.constructor.call(this, _a);
   }
   Ball.prototype.play = function () {
       this.superClass.play.call(this);//执行超类的play方法
       console.log("end");
   }
   Object.defineProperty(Ball.prototype, "d", {
       value: 20
   })
   Ball.extend(Box);

   var b=new Ball(10);
   console.log(b);


是目前最成熟的继承方式,babel对ES6继承的转化也是使用了寄生组合式继承

我们回顾一下实现过程:

原型链继承,通过把子类实例的原型指向父类实例来继承父类的属性和方法;但缺陷在于,对子类实例继承的引用类型的修改会影响到所有的实例对象以及无法向父类的构造方法传参。
因此我们引入了构造函数继承, 通过在子类构造函数中调用父类构造函数并传入子类this来获取父类的属性和方法,但缺陷在于,构造函数继承不能继承到父类原型链上的属性和方法。
综合了两种继承的优点,提出了组合式继承,但组合式继承也引入了新的问题,它每次创建子类实例都执行了两次父类构造方法,
我们通过将子类原型指向父类实例改为子类原型指向父类原型的浅拷贝来解决这一问题,也就是最终实现 —— 寄生组合式继承
————————————————
版权声明:本文为CSDN博主「ghl-Dragon」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/g1437353759/article/details/109294736

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值