史上最为详细的javascript继承(推荐)

时间:2021-05-26

前言

为大家分享js中最常见最详细的继承方式,接下来将一下面的几个维度进行展示说明
文章有点长,请耐心阅读😁,有什么错误理解的地方希望留言指出来

  • 产生原因
  • 代码实现
  • 基本原理
  • 语言实现
  • 场景优点
  • 缺点
  • 继承方式

  • 原型链继承
  • 借用构造函数模式继承
  • 组合继承
  • 原型式继承
  • 寄生式继承
  • 寄生组合
  • 原型链继承
    相信小伙伴们都知道到原型链继承(ECMAScript 中描述了原型链的概念,并将原型链作为实现继承的主要方法),因为原型链继承非常的强大,但是也有它的缺点,接下来咱们就按照上面的维度看看原型链继承到底是什么鬼
    代码实现:(需要两个构造函数来完成一个原型链继承)

    // SuperType 构造函数称为超类 function SuperType (){ this.name='super'; this.friend=[]; this.property = true; }SuperType.prototype.getName=function(){ return this.name;}SuperType.prototype.getSuperValue = function(){ return this.property;}; // SubType 构造函数称为子类function SubType(name,age){ this.name=name; this.age=age; this.subproperty = false; }SubType.prototype=new SuperType();SubType.prototype.constrcutor=SubType;SubType.prototype.getAge=function(){ return this.age;}SubType.prototype.getSubValue = function (){ return this.subproperty;}; var child = new SubType('shiny',12);console.log(child.getName)//shinyconsole.log(child.getAge())//12

    图解部分 属性

    基本原理

    使用类似作用域的原型链,进行继承查找

    语言实现

    定义两个构造函数,分别为父类(SuperType)、子类(SubType),为了实现子类能够使用父类的属性(本身和原型上面的属性)。重写子类的原型,让子类的原型指向父类实例,这样子类的构造函数就是父类的实例地址,实现子类可以使用父类的本身和原型上的属性

    优点

    子类可以通过原型链的查找,实现父类的属性公用与子类的实例

    缺点

  • 一些引用数据操作的时候会出问题,两个实例会公用继承实例的引用数据类
  • 谨慎定义方法,以免定义方法也继承对象原型的方法重名
  • 无法直接给父级构造函数使用参数
  • 借用构造函数模式继承
    虽然原型链继承很强大但是也有他的缺点,借用构造函数继承可以解决原型链继承的缺点,开线面的解释
    代码实现:

    // 把父类当中一个函数使用function SuperType(name){this.name=namethis.friend=['a','b']}SuperType.prototype.getFriend=function(){ return this.firend}function SubType(name){ // 执行父类函数 SuperType.call(this,name);}var child = new SubType('shiny')var childRed = new SubType('red')console.log(child.name)//shinyconsole.log(childRed.name)//redchild.firend.push('c')console.log(child.friend)//a,b,cconsole.log(childRed.friend)//a,bconsole.log(childRed.getFriend)//undefined

    基本原理

    使用call apply方法,通过执行方法修改tihs (上下文),是的父级的this变成子类实例的this,这样每个实例都会得到父类的属性,实现引用属性备份

    使用场景

    父类中需要一些子类使用共享的引用类型,并且子类可能会操作父类共享的引用类型
    但是父类的非this绑定的属性和方法是不可以使用的(放在父类prototype的属性和方法)

    语言实现

    不要把父类当中构造函数,当中一个函数来处理这样更容易理解,在子类的构造函数中借用父类函数通过修改this来执行,这样子类的实例包含父类的属性

    优点

  • 解决了原型链继承的 引用类型操作问题
  • 解决了父类传递参数问题
  • 缺点

  • 仅仅使用借用构造函数模式继承,无法摆脱够着函数。方法在构造函数中定义复用不可谈
  • 对于超类的原型定义的方法对于子类是不可使用的,子类的实例只是得到了父类的this绑定的属性
  • 考虑到这些缺点,单独使用借用构造函数也是很少使用的
  • 组合继承

    上面的两种继承方式(原型链继承+借用构造函数继承),都有自己优缺点,但是他们不是很完美,下面解释一下组合继承

    代码实现:

    function SuperType(name){ this.name=name; this.firend=['a','b']}SuperType.prototype.getName=function(){ return this.name}function SubType(name,age){ this.age=age; SuperType.call(this,name)}SubType.prototype=new SuperType();SubType.prototype.constrcutor = SubType;SubType.prototype.getAge=function(){ return this.age}var childShiny=new SubType('shiny',23);var childRed = new SubType('red',22);childShiny.firend.push('c');childRed.firend.push('d');console.log(childShiny.getName());console.log(childShiny.getAge());console.log(childRed.getName());console.log(childRed.getAge());console.log(childRed.friend);//[a,b,d]console.log(childShiny.friend);//[a,b,c]

    基本原理

    使用原型链的继承实现,通过原型查找功能来满足原型链共享方法
    使用借用构造函数方法,使用实例备份父类共享引用类型备份

    使用场景

    得到原型链继承和构造函数继承的优点,是被开发人员认可的一种继承方式,但是也有他的缺点
    语言实现

    定义两个构造函数,分别为父类(SuperType)、子类(SubType),为了实现子类能够使用父类的属性(本身和原型上面的属性)。重写子类的原型,让子类的原型指向父类实例,这样子类的构造函数就是父类的实例地址,实现子类可以使用父类的本身和原型上的属性
    不要把父类当中构造函数,当中一个函数来处理这样更容易理解,在子类的构造函数中借用父类函数通过修改this来执行,这样子类的实例包含父类的属性

    优点

  • 解决了原型链继承引用类型的实例操作导致引用改变
  • 解决了借构造函数继承方式的,父类原型子类实例可以使用
  • 缺点

  • 父类的构造函数被实例换了两次
  • 实例会有父类的构造函数的一些this属性、子类的构造函数(prototype)上也有一份实例的上有的属性
  • 原型式继承

    话说上面的的组合继承不是已经被开发者认可了吗,原型式继承是啥?下面咱们看看原型式继承是什么样的。

    代码实现:

    1 function object(o){ function F(){}; F.prototype=o; return new F()}var person = {name: "Nicholas",friends: ["Shelby", "Court", "Van"]};var personShiny = object(person);var personRed = object(person);console.log(personShiny.name)//Nicholasconsole.log(personRed.name)//NicholaspersonShiny.friends.push('red');personRed.friends.push('shiny');console.log(personShiny.friends)//["Shelby", "Court", "Van","red","shiny"]//ECMAScript 5 通过新增 Object.create()方法规范化了原型式继承。这个方法接收两个参数:一//个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象。在传入一个参数的情况下,//Object.create()与 object()方法的行为相同。2 var person = {name: "Nicholas",friends: ["Shelby", "Court", "Van"]};var personShiny = Object.create(person);var personRed = Object.create(person);console.log(personShiny.name)//Nicholasconsole.log(personRed.name)//NicholaspersonShiny.friends.push('red');personRed.friends.push('shiny');console.log(personShiny.friends)//["Shelby", "Court", "Van","red","shiny"]

    基本原理

    通过Object.create()方法来创建一个有基础类的实例,这实例的__proto__指向基础类

    使用场景

    在不使用构造函数的情况下,只想让一个对象与另一个对象保持类似的情况下

    语言实现

    需要创建一个基础对象,作为一个新对象的基础对象,通过object方法或者Object.create方法处理得到一个新实例,这个新实例上的__proto__指向基础对象

    优点

    再不用创建构造函数的情况下,实现了原型链继承,代码量减少一部分

    缺点

  • 一些引用数据操作的时候会出问题,两个实例会公用继承实例的引用数据类
  • 谨慎定义方法,以免定义方法也继承对象原型的方法重名
  • 无法直接给父级构造函数使用参数
  • 寄生继承

    咱们看了上面的原型式继承,其实就是和原型链继承差别不大,只是省去了构造函数这一部,但是原型式继承也是有缺点的(不能够给备份的对象添加属性),下面寄生继承来解决。

    代码实现:

    // 和工厂模式非常类似,创建一个对象,增强一些功能并返回该对象function createAnother(o){ var clone = Object(o); clone.sayHi=function(){ console.log('hi')} return clone}var person = { name:'shiny', friends:['a','b']}var personShiny = createAnother(person);console.log(personShiny.sayHi())//Ho

    基本原理

    备份一个对象,然后给备份的对象进行属性添加,并返回

    使用场景

    在考不使用构造函数的情况下实现继承,前面示
    范继承模式时使用的 object()函数不是必需的;任何能够返回新对象的函数都适用于此模式

    语言实现

    类似构造函数,通过一个执行方法,里面创建一个对象,为该对象添加属性和方法,然后返回

    优点

  • 再不用创建构造函数的情况下,实现了原型链继承,代码量减少一部分
  • 可以给备份的对象添加一些属性
  • 缺点

    类似构造函数一样,创建寄生的方法需要在clone对象上面添加一些想要的属性,这些属性是放在clone上面的一些私有的属性

    寄生组合继承

    咱们看了上面的组合继承看上去已经很完美了,但是也有缺点(父类被实例化两次、子类实例和子类的构造函数都有相同的属性),寄生组合就是来解决这些问题的

    代码实现:

    function inheritPrototype({SubType,SuperType}){ const prototype = Object(SuperType.prototype); prototype.constrcutor=SubType; SubType.prototype=prototype;}function SuperType(name){ this.name=name; this.friends=['a','b']}SuperType.prototype.getName=function(){ return this.name;}function SubType(name,age){ this.age=age; SuperType.call(this,name)}inheritPrototype({SubType,SuperType});SubType.prototype.getAge=function(){ return this.age}var SubTypeShiny = new SubType('Shiny',23);SubTypeShiny .friends.push('c')var SubTypeRed = new SubType('Red',21);SubTypeRed .friends.push('d')console.log(SubTypeShiny.getName())//Shinyconsole.log(SubTypeShiny.getAge())//22console.log(SubTypeShiny.friends)//['a','b','c']console.log( SubTypeRed.getName())//Redconsole.log( SubTypeRed.getAge())//21console.log( SubTypeRed.friends)//['a','b','d']

    基本原理

    子类构造函数内通过call、apply方法进行修改父类构造函数的this和执行父类构造函数,使的子类的实例拥有父类构造函数的一些属性,
    结合子类的原型修改成父类构造函数的原型,并把父类的原型的constructor指向子类构造函数

    使用场景

    在考不使用构造函数的情况下实现继承,前面示

    范继承模式时使用的 object()函数不是必需的;任何能够返回新对象的函数都适用于此模式

    语言实现

    极度类似组合寄生方式,只是修改了子类原型链继承的方式,组合寄生是继承父类的实例,寄生组合寄生则是通过一子类的原型继承父类的原型,并把该原型的constructor指向子类构造函数

    优点

  • 在少一次实例化父类的情况下,实现了原型链继承和借用构造函数
  • 减少了原型链查找的次数(子类直接继承超类的prototype,而不是父类的实例)
  • 缺点

    暂无

    下面是组合继承和寄生组合继承的原型图对比

    以上所述是小编给大家介绍的javascript继承详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!

    声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。

    相关文章