一篇文章教你JS函数继承

目录
  • 一. 前言:
  • 二.原型链继承:
  • 三.借用构造函数继承(对象伪装):
  • 四.组合继承:
  • 五.寄生组合继承:
  • 六.class继承:
  • 七.总结:

一. 前言:

Hello,大家最近过得好吗,😃。函数继承是在JS里比较基础也是比较重要的一部分,而且也是面试中常常要问到的。下面带你快速了解JS中有哪几种是经常出现且必须掌握的继承方式。掌握下面的内容面试也差不多没问题啦~

当然,这需要一定的原型链基础,对原型链不熟悉的可以看我这篇文章👉:速识js原型链。

二.原型链继承:

原型链继承的要点在于父类的实例作为子类的原型。直接看下面这个例子:

 // 父函数 Person
  function Person(name, age) {
    // 定义一些属性
    this.name = name;
    this.age = age;
    this.nature = ["auroras", "wind", "moon"];
  }
 // 定义Person原型上的一个方法
  Person.prototype.sayLove = function () {
    console.log(this.name + " like " + this.nature[0]);
  };
  // 子函数 Jack
  function Jack() {}
  // 父类的实例作为子类的原型 (-------------------实现核心--------------------------)
  Jack.prototype = new Person();

现在我们创建两个Jack 的实例,测试看是否实现了继承Person:

      var jack1 = new Jack();
      var jack2 = new Jack();
      jack2.nature[0] = "sea";
      jack1.sayLove();
      jack2.sayLove();
      console.log(jack1.nature);
      console.log(jack2.nature);

看运行结果确实继承了,能执行sayLove方法。但有甚多缺点,创建Jack实例的时候传递不了参数name和age,而且不同实例间nature引用类型属性相互影响,一个改变那都改变:

三.借用构造函数继承(对象伪装):

核心在于“盗用构造函数”(constructor stealing)。在子类构造函数中调用父类构造函数。因为毕竟函数就是在特定上下文中执行代码的简单对象,所以可以使用apply()和call()方法以新创建的对象为上下文执行构造函数。它能解决原型链继承中传参数和引用类型属性冲突。还是直接看例子:

 // 父函数 Person
  function Person(name, age) {
    // 定义一些属性
    this.name = name;
    this.age = age;
    this.nature = ["auroras", "wind", "moon"];
  }
 // 定义Person原型上的一个方法
  Person.prototype.sayLove = function () {
    console.log(this.name + " like " + this.nature[0]);
  };
  // 子函数 Lucy
  function Lucy(name, age) {
  // 通过call把this指向Lucy,相当于拷贝了一份父函数 Person 里的内容(---------实现核心--------------)
        Person.call(this, name, age);
      }
  //给子函数原型上也定义一个方法
   Lucy.prototype.syaName = function () {
        console.log("My name is " + this.name);
      };

现在我们创建两个Lucy 的实例,测试看是否实现了继承Person:

      var lucy1 = new Lucy("lucy1", "20");
      var lucy2 = new Lucy("lucy2", "22");
      lucy2.nature[0] = "sea";
      console.log(lucy1.name);
      console.log(lucy1.nature);
      console.log(lucy2.nature);
      lucy1.syaName();
      lucy2.syaName();
      lucy1.sayLove();

结果看可以继承了,能传参数,引用类型属性也不互相影响,但是缺点显而易见,可以看到报错,无法使用父类的原型上的方法sayLove。

四.组合继承:

组合继承就是结合了原型链继承和借用构造函数继承两者的核心实现的一种继承方法,既能传递参数,引用类型属性也互不影响,同时子类也能获取得到父类的方法。这也是目前比较常用的继承方式。直接看例子:

 // 父函数 Person
  function Person(name, age) {
    // 定义一些属性
    this.name = name;
    this.age = age;
    this.nature = ["auroras", "wind", "moon"];
  }
 // 定义Person原型上的一个方法
  Person.prototype.sayLove = function () {
    console.log(this.name + " like " + this.nature[0]);
  };
  // 子函数Lisa
      function Lisa(name, age) {
// 通过call把this指向Lisa,相当于拷贝了一份父函数 Person 里的内容(------实现核心-----------)
        Person.call(this, name, age);
      }
// 父类的实例作为子类的原型 (--------------实现核心-------------------)
      Lisa.prototype = new Person();
  //小知识点,这里是让Lisa的constructor重新指向Lisa,不然因为Lisa的原型为Person实例,constructor会指向Person
      Lisa.prototype.constructor = Lisa;

现在我们创建两个Lisa 的实例,测试看是否实现了继承Person:

      var lisa1 = new Lisa("lisa1", "20");
      var lisa2 = new Lisa("lisa2", "21");
      lisa2.nature[0] = "sea";
      console.log(lisa1.name);
      console.log(lisa1.nature);
      console.log(lisa2.nature);
      lisa1.sayLove();
      lisa2.sayLove();

可以看到基本上实现了我们继承的功能。也修补了原型链和借用构造函数继承的缺点。但是呢,它还是有一个小缺点,就是可以看到在代码注释实现核心那,两次都调用了Person,那么Lisa原型上和实例上有了两份相同的属性,那就会多少有一些性能浪费。

五.寄生组合继承:

其实寄生组合继承和组合继承差不多的,就是多了一个解决组合继承上原型和实例产生两份相同属性的缺点。解决核心是我们既然只是想要子类原型赋值为父类原型,那没必要new一个父类实例。直接创造一个新对象,它值为父类的原型,再将它赋值给子类原型就行了。

其中用到Object.create(proto,[propertiesObject])这个方法创建一个新对象。相当于新对象的__proto__为其参数proto。当然Object.create可能低版本ie没有,所以下面也自定义封装了Object.create方法,当然只是简单封装。直接看例子:

 // 父函数 Person
  function Person(name, age) {
    // 定义一些属性
    this.name = name;
    this.age = age;
    this.nature = ["auroras", "wind", "moon"];
  }
 // 定义Person原型上的一个方法
  Person.prototype.sayLove = function () {
    console.log(this.name + " like " + this.nature[0]);
  };
  // 子函数 Andy
 function Andy(name, age) {
        Person.call(this, name, age);
      }
// 如果没有 Object.create()方法,简单封装下
      if (!Object.create) {
        Object.create = function (proto) {
          function Temp() {}
          Temp.prototype = proto;
          return new Temp();
        };
      }
// 调用Object.create方法,新建一对像,其__proto__为Person.prototype,并赋值给 Andy.prototype (-------实现核心----------)
      Andy.prototype = Object.create(Person.prototype);
  //修改constructor指向
      Andy.prototype.constructor = Andy;

现在我们创建两个Andy的实例,测试看是否实现了继承Person:

      console.log(Andy.prototype.__proto__ === Person.prototype);
      var andy1 = new Andy("andy1", "20");
      var andy2 = new Andy("andy2", "21");
      andy2.nature[0] = "sea";
      console.log(andy1.name);
      console.log(andy1.nature);
      console.log(andy2.nature);
      andy1.sayLove();
      andy2.sayLove();

完美运行:

六.class继承:

ES6出了class语法糖之后,就可以通过class定义类并实现类的继承。直接看例子:

//定义一个父类 Animal
  class Animal {
   //这里constructor指向类本身,跟es5行为一样的
    constructor(name) {
      this.name = name;
    }
    likeEat() {
      console.log(this.name + " like eat " + this.food);
    }
  }
//定义一个子类 Dog ,通过 extends 继承父类Animal
  class Dog extends Animal {
    constructor(name, food) {
      //通过super(属性名)继承父类属性
     super(name);
     this.food = food;
    }
    likeEat() {
     //通过super.+父类方法 实现继承父类方法
      super.likeEat();
    }
  }

new一个Dog实例,测试看看,Dog是否继承了Animal:

  var jinmao = new Dog("jinmao", "bone");
  console.log(jinmao.name);
  jinmao.likeEat();

可以看到完美实现了:

七.总结:

方法 优点 缺点
原型链继承 能继承父原型上属性方法等等… 无法传参、引用类型属性冲突等等…
借用构造函数继承 可以传参,引用类型属性不冲突等等… 无法继承父原型上方法等等…
组合继承 有上面两种的优点,并解决其缺点 调用两次父实例产生两份相同属性等等…
寄生组合继承 有上面三种优点,并解决其缺点 可能不太直观等等…
class继承 es6新语法,简洁直观等等… 低版本ie不支持es6等等…

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • javascript 用函数实现继承详解

    一.知识储备: 1.枚举属性名称的函数: (1)for...in:可以在循环体中遍历对象中所有可枚举的属性(包括自有属性和继承属性) (2)Object.keys():返回数组(可枚举的自有属性) (3)Object.getOwnPropertyNames():所有的自有属性 3.属性的特性:数据属性和存取器属性 (1)数据属性:可写(writable)  可枚举(enumerable)  可配置(configurable)  值(value) 数据属性只有一个简单的值: (2)存取器属性: 写

  • Javascript面向对象编程(二) 构造函数的继承

    今天要介绍的是,如何生成一个"继承"多个对象的实例. 比如,现在有一个"动物"对象的构造函数, 复制代码 代码如下: function Animal(){ this.species = "动物"; } 还有一个"猫"对象的构造函数, 复制代码 代码如下: function Cat(name,color){ this.name = name; this.color = color; } 怎样才能使"猫"继承&

  • JavaScript如何借用构造函数继承

    这篇文章主要介绍了JavaScript如何借用构造函数继承,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 借用构造函数继承是在子类型构造函数的内部调用超类型构造函数,通过使用apply()和call()方法 function girlFriend(){ this.girls = ['chen','wang','zhu']; } function Person(){ girlFriend.call(this,20); } var wang = n

  • JS函数进阶之继承用法实例分析

    本文实例讲述了JS函数进阶之继承用法.分享给大家供大家参考,具体如下: 直接代码,不解释: <html> <head> <title>js函数继承进阶</title> <meta charset="UTF-8"/> <script type="text/javascript"> function person(name,age){ //对象的创建 this.name=name; this.age

  • Javascript面向对象编程(三) 非构造函数的继承

    今天是最后一个部分,介绍不使用构造函数实现"继承". 一.什么是"非构造函数"的继承? 比如,现在有一个对象,叫做"中国人". 复制代码 代码如下: var Chinese = { nation:'中国' }; 还有一个对象,叫做"医生". 复制代码 代码如下: var Doctor ={ career:'医生' } 请问怎样才能让"医生"去继承"中国人",也就是说,我怎样才能生成一个&

  • 一篇文章教你JS函数继承

    目录 一. 前言: 二.原型链继承: 三.借用构造函数继承(对象伪装): 四.组合继承: 五.寄生组合继承: 六.class继承: 七.总结: 一. 前言: Hello,大家最近过得好吗,

  • 一篇文章教你学会js实现弹幕效果

    目录 新建一个html文件: 建好html文件,搞出初始模版 HTML添加 CSS填充 js逻辑代码 动画效果 下面是弹幕效果 : 相信小伙伴们都看过了,那么它实现的原理是什么呢,那么我们前端怎么用我们web技术去实现呢?? 新建一个html文件: 哈哈哈,大家别像我一样用中文命名. 中文命名是不合规范的,行走江湖,大佬们看见你的中文命名会笑话你的. 上图中,我们引入了jquery插件,没错我们用jq写,回归原始(找不到cdn链接的小伙伴可以百度bootcdn,在里面搜索jquery).并且取了

  • 一篇文章教你使用SpringBoot如何实现定时任务

    前言 在 Spring + SpringMVC 环境中,一般来说,要实现定时任务,我们有两中方案,一种是使用 Spring 自带的定时任务处理器 @Scheduled 注解,另一种就是使用第三方框架 Quartz ,Spring Boot 源自 Spring+SpringMVC ,因此天然具备这两个 Spring 中的定时任务实现策略,当然也支持 Quartz,本文我们就来看下 Spring Boot 中两种定时任务的实现方式. 一.第一种方式:@Scheduled 使用 @Scheduled

  • 一篇文章教你学会使用Python绘制甘特图

    目录 优点 局限 一日一书 用来制作甘特图的专业工具也不少,常见的有:Microsoft Office Project.GanttProject.WARCHART XGantt.jQuery.Gantt.Excel等,网络上也有一些优质工具支持在线绘制甘特图. 可是这种现成的工具,往往也存在一些弊端,让编程人员不知所措.比如说,花里胡哨的UI,让人目不暇接,不知点哪个才好: 比如说,有些基于浏览器的图表需要掌握HTML.JS等编程语言,只会点Python的我直接被劝退: 再比如,进来就是注册.登

  • 一篇文章教你用C语言模拟实现字符串函数

    目录 前言 模拟 1.strlen 函数 2.strcpy 函数 3.strcat 函数 4.strcmp函数 5.strncpy函数 6.strncat函数 7.strncmp函数 8.strstr函数 9.strtok函数 总结 前言 编程过程中经常会使用到一些字符串函数,这些字符串函数都在C语言标准库中,我们可以直接使用.但我们也要了解一下它们是如何实现的. 模拟 1.strlen 函数 strlen函数是用来求字符串长度的.官方给出的解释如图 返回值类型是无符号整型,参数类型是char*

  • 一篇文章弄懂js中的typeof用法

    目录 基础 返回类型 string 和 boolean number 和 bigint symbol undefined function object 其他 常见问题 引用错误 typeof null typeof 的局限性 扩展:BigInt 类型 总结 基础 typeof 运算符是 javascript 的基础知识点,尽管它存在一定的局限性(见下文),但在前端js的实际编码过程中,仍然是使用比较多的类型判断方式. 因此,掌握该运算符的特点,对于写出好的代码,就会起到很大的帮助作用. typ

  • 一篇文章教你3分钟如何发布Qt程序

    导读:Qt程序编写好以后该如何发布.本文教你使用Qt自带工具windeployqt来进行操作. 本文字数:500,阅读时长大约:3分钟 (1)编写一个简单的程序 我们先做一个简单的窗口,添加一个图片资源文件,放置到窗口当中. 选择添加Qt Resource File文件类型 选择资源文件的路径,并为它命名 点击完成 设置资源前缀,如果资源层次不是很复杂的话,可以只设置一层,用"/"表示 点击Add Files添加一个图片文件 在主窗口中添加一个 Tool Button,设置刚才的图片为

  • 一篇文章教你用python画动态爱心表白

    初级画心 学Python,感觉你们的都好复杂,那我来个简单的,我是直接把心形看作是一个正方形+两个半圆: 于是这就很简单了,十行代码解决: import turtle as t t.pensize(2) # 笔大小2像素 t.pencolor("red") # 颜色为红色 t.left(45) # 45度 t.fd(200) # 向前200直线 t.circle(100, 180) # 画一圆半径100 弧度180 t.right(90) # 向右90度 t.circle(100, 1

  • 一篇文章教你使用枚举来实现java单例模式

    目录 传统的单例写法解决了什么问题 仍然存在的问题 为什么枚举就没有问题 总结 传统的单例写法解决了什么问题 首先,在大多数情况下(不包含面试),传统的单例写法已经完全够用了.通过 synchronized 关键字解决了多线程并发使用. public synchronized static SingleClassV1 getInstance(){ if(instance == null){ instance = new SingleClassV1(); } return instance; }

  • 一篇文章带你入门Java继承

    目录 Java中继承 什么是继承: 为什么要用继承: 学习总结: 继承关键字:extends 总结 Java中继承 什么是继承: 继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为. 为什么要用继承: 可以去掉重复代码,方便后期维护 举个列子,大家应该都玩过英雄联盟,每个英雄都是一个类,如果说不用继承的话每次都要重复定义每个英雄的成员属性,如下图我举了一个MF,一个EZ的列子 public class MissFortu

随机推荐