详解JavaScript实现继承的五种经典方式(附图解)

 更新时间:2023年08月11日 09:42:36   作者:自律版Zz  
JavaScript中的继承是一种机制,通过它可以创建一个对象,该对象可以享有另一个对象的属性和方法,本文将详细的为大家介绍实现继承的五种经典方式,感兴趣的小伙伴跟着小编一起来看看吧

前言

  • 在 JavaScript 中,实现继承的几种常见方式包括:
      1. 原型链继承: 此种方式也有两种方式
      • 1.1 共享同一个父类实例
      • 1.2 绕过父类实例,共享同一个类型原型
      1. 构造函数继承(借用构造函数)
      1. 组合继承
      1. 原型式继承
      1. ES6 类继承

原型链继承

原型链继承-共享同一个父类实例

这是 JavaScript 最早的继承方式,通过将子类的原型对象指向父类的实例,实现子类继承父类的属性和方法。但它有一个缺点,就是所有子类实例共享同一个父类实例。

function Parent() {
  this.property = "parentProperty";
}
Parent.prototype.say = function () {
  console.log("Parent say");
};
function Child() {
  this.childProperty = "childProperty";
}
// 修改 Child 的 prototype 属性指向 Parent 实例对象,那么 Child 实例对象的 __proto__ 就会指向其构造函数 Child 的 prototype 属性(即Parent 实例对象)
// 修改了 Child.prototype 的指向后,那么原来 Child.prototype 指向的对象由于被没有引用,就会被回收。
Child.prototype = new Parent();
const childInstance = new Child();
console.log(childInstance.property); // 输出 'parentProperty'
childInstance.say();

下图是代码图解:

48.png

上面的代码看似没有问题,但其实还是存在缺陷:

console.log(Child.prototype.constructor); // 输出:[Function: Parent], 即是 Parent 构造函数,这是由于 Child.prototype 自身没有,就会沿着 __proto__ 寻找,因此找到 Parent。这明显是不对的
console.log(Child.prototype.constructor === Child); // 输出:false, 这明显也是不对的

因此我们需要再修改 Child.prototype 的指向之后(即代码 Child.prototype = new Parent(); ),需要同时修改 Child.prototype.constructor 的指向:

Child.prototype = new Parent();
+Child.prototype.constructor = Child;
  • 后续代码也是同理!

原型链继承-绕过父类实例,共享同一个父类的原型

function Parent() {
  this.property = "parentProperty";
}
Parent.prototype.say = function () {
  console.log("Parent say");
};
function Child() {
  Parent.call(this);
  this.childProperty = "childProperty";
}
// 方式一:直接指向
// Child.prototype.__proto__ = Parent.prototype;
// 方式二:使用 Object.create(),这是 es5 的方法
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
let childInstance = new Child();
console.log(childInstance.property);
childInstance.say();

下图是代码图解:

51.png

构造函数继承(借用构造函数)

这种方式通过在子类构造函数中调用父类构造函数,实现继承属性。这样每个子类实例都拥有独立的属性副本,但无法继承父类原型上的方法。

function Parent() {
  this.property = "parentProperty";
}
Parent.prototype.say = function () {
  console.log("Parent say");
};
function Child() {
  Parent.call(this);
  this.childProperty = "childProperty";
}
const childInstance = new Child();
console.log(childInstance.property); // 输出 'parentProperty'
// childInstance.say() // 报错:childInstance.say is not a function

下图是代码图解:

49.png

组合继承

组合继承结合了原型链继承和构造函数继承,通过在子类构造函数中调用父类构造函数,然后设置子类的原型为一个父类实例,实现了既能继承属性又能继承方法。

function Parent() {
  this.property = "parentProperty";
}
Parent.prototype.say = function () {
  console.log("Parent say");
};
function Child() {
  Parent.call(this);
  this.childProperty = "childProperty";
}
Child.prototype = new Parent();
// 注意:修改其原型对象之后,同时必须得修改 constructor 的指向
Child.prototype.constructor = Child;
const childInstance = new Child();
console.log(childInstance.property); // 输出 'parentProperty'
childInstance.say();

52.png

原型式继承

这种继承方式创建一个临时的构造函数,将这个构造函数的原型指向父构造函数的原型,再将子构造函数的原型指向该构造函数的实例,从而实现继承。

function mockExtend(Parent, Child) {
  function Fn() {}
  /**
   * 1. 修改了 Fn.prototype 的指向后,那么原来的 Fn.prototype 没有被引用,则会被回收
   * 2. 那么Fn的实例对象的 __proto__ 就指向其构造函数的 prototype
   */
  Fn.prototype = Parent.prototype;
  Child.prototype = new Fn();
  // 注意:修改了原型对象之后,同时必须得修改 constructor 的指向
  Child.prototype.constructor = Child;
}
// =============================== 使用 ==================================
function Parent() {
  this.property = "parentProperty";
}
Parent.prototype.say = function () {
  console.log("Parent say");
};
function Child() {
  Parent.call(this);
  this.childProperty = "childProperty";
}
mockExtend(Parent, Child);
const childInstance = new Child();
console.log(childInstance.property);
childInstance.say();

下图是代码图解:

50.png

ES6 类继承

ES6 引入了 class 关键字,使继承更加易读和易用。通过 extends 关键字,一个类可以继承另一个类的属性和方法。

class Parent {
  constructor() {
    this.property = "parentProperty";
  }
  say() {
    console.log("Parent say");
  }
}
class Child extends Parent {
  constructor() {
    super();
    this.childProperty = "childProperty";
  }
}
const childInstance = new Child();
console.log(childInstance.property); // 输出 'parentProperty'
childInstance.say();

到此这篇关于详解JavaScript实现继承的五种经典方式(附图解)的文章就介绍到这了,更多相关JavaScript实现继承方式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • js实现拖拽元素选择和删除

    js实现拖拽元素选择和删除

    这篇文章主要为大家详细介绍了js实现拖拽元素选择和删除,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-08-08
  • glsl_buffer实现渐变三角形方法详解

    glsl_buffer实现渐变三角形方法详解

    这篇文章主要为大家介绍了glsl_buffer实现渐变三角形方法详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-03-03
  • Ajax 文件上传进度监听之upload.onprogress案例详解

    Ajax 文件上传进度监听之upload.onprogress案例详解

    这篇文章主要介绍了Ajax 文件上传进度监听之upload.onprogress案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-09-09
  • 深入理解JSON数据源格式

    深入理解JSON数据源格式

    JSON 在很多场合下作为数据格式比XML要更加方便。JSON的数据由对象、数组和元素等格式组成。每种格式都可以包含合法的JavaScript数据类型
    2014-01-01
  • open 动态修改img的onclick事件示例代码

    open 动态修改img的onclick事件示例代码

    动态修改img的onclick事件,使用open也可轻松做到,下面有个不错的示例,需要的朋友可以参考下
    2013-11-11
  • javascript实现百度地图鼠标滑动事件显示、隐藏

    javascript实现百度地图鼠标滑动事件显示、隐藏

    这篇文章主要介绍了javascript实现百度地图鼠标滑动事件显示、隐藏的思路和方法,十分的实用,这里推荐给小伙伴们,有需要的朋友可以参考下。
    2015-04-04
  • JS实现简单易用的手机端浮动窗口显示效果

    JS实现简单易用的手机端浮动窗口显示效果

    这篇文章主要介绍了JS实现简单易用的手机端浮动窗口显示效果,涉及javascript针对页面元素的动态操作相关技巧,适用于做广告展示,需要的朋友可以参考下
    2016-09-09
  • JavaScript实现LI列表数据绑定的方法

    JavaScript实现LI列表数据绑定的方法

    这篇文章主要介绍了JavaScript实现LI列表数据绑定的方法,可实现绑定Li列表项对应数值项的功能,涉及javascript鼠标onmousemove、onmouseout及onclick等事件的相关使用技巧,需要的朋友可以参考下
    2015-08-08
  • 使用Modello编写JavaScript类

    使用Modello编写JavaScript类

    使用Modello编写JavaScript类...
    2006-12-12
  • JavaScript 实现模态对话框 源代码大全

    JavaScript 实现模态对话框 源代码大全

    对话框在Windows应用程序中使用非常普遍,许多应用程序的设定,与用户交互需要通过对话框来进行,因此对话框是Windows应用程序中最重要的界面元素之一,是与用户交互的重要手段。
    2009-05-05

最新评论