JavaScript实现类继承的方法最全讲解

 更新时间:2025年09月14日 11:30:55   作者:鹧鸪yy  
JavaScript 类继承通过extends和super关键字提供了一种直观的面向对象编程方式,基于原型链实现,这篇文章主要介绍了JavaScript实现类继承的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

先贴一张图:

1、原型链继承

原型链继承的核心思想是让子类的 prototype 指向父类的实例,这样子类就能访问父类的方法和属性。

缺点:

  1. 子类实例共享父类的引用属性:修改 child1.colors 之后,child2.colors 也会受到影响,因为 colors 是父类实例的属性,所有子类实例都共享同一个对象。
  2. 无法向父类构造函数传参:创建子类实例时,无法向 Parent 传递参数。

例子:

function Parent() {
  this.name = "Parent";
  this.colors = ["red", "blue", "green"];
}

Parent.prototype.getName = function() {
  return this.name;
};

function Child() {}

Child.prototype = new Parent(); // 关键点:让子类的 prototype 指向父类实例

const child1 = new Child();
console.log(child1.getName()); // Parent
console.log(child1.colors); // ["red", "blue", "green"]

child1.colors.push("yellow");

const child2 = new Child();
console.log(child2.colors); // ["red", "blue", "green", "yellow"] (被修改了)

2、构造函数

通过在子类的构造函数中调用父类构造函数,并使用 call()apply() 绑定 this,从而避免原型链继承的问题。

缺点:

  1. 子类无法继承父类在原型链上的属性和方法。
  2. 每个实例都拷贝一份,占用内存大,尤其是方法过多的时候。(函数复用又无从谈起了,本来我们用 prototype 就是解决复用问题的)

优点:解决了通过原型链继承子类对于父类引用类型属性的修改,导致其他子类实列共享了修改的问题

例子:

function Parent(name) {
  this.name = name;
  this.colors = ["red", "blue", "green"];
}

function Child(name) {
  Parent.call(this, name); // 关键点:在子类构造函数中调用父类构造函数
}

const child1 = new Child("Child1");
const child2 = new Child("Child2");

child1.colors.push("yellow");

console.log(child1.colors); // ["red", "blue", "green", "yellow"]
console.log(child2.colors); // ["red", "blue", "green"] (不会被修改)

3、组合继承

组合继承(原型链继承+借用构造函数继承)由于这两种继承方式都存在各自的优缺点,从而将他们优点结合起来,通过原型继承父类原型上的属性和方法,通过构造函数的方法继承父类的属性

缺点:组合继承是js最常用的继承模式,组合继承最大的问题就是无论在什么情况下,都会调用两次构造函数:一次是在创建子类型原型时,另一次是在子类构造函数内部。

优点:就是可以把方法定义在原型上复用,每个实例又有自己的属性。组合继承弥补了原型链和盗用构造函数的不足,是js中使用最多的继承模式。

例子:

function Parent(name) {
  this.name = name;
  this.colors = ["red", "blue", "green"];
}

Parent.prototype.getName = function() {
  return this.name;
};

function Child(name, age) {
  Parent.call(this, name); // 调用父类构造函数,继承实例属性
  this.age = age;
}

Child.prototype = new Parent(); // 继承父类方法
Child.prototype.constructor = Child; // 修正 constructor 指向

const child1 = new Child("Child1", 18);
console.log(child1.getName()); // Child1
console.log(child1.colors); // ["red", "blue", "green"]

解决的问题:

  1. 避免了引用属性共享问题
  2. 支持传参
  3. 方法可以复用

存在的问题:

  • 父类构造函数被调用了两次

4、寄生组合继承

组合继承存在这一定的效率问题,它的父类构造函数始终会被调用俩次,一次在创建字类原型时调用,另一次在子类构造函数中调用。本质上子类只需要在执行时重写自己的原型就行了。寄生式组合继承可以算是引用类型继承的最佳模式。

寄生组合继承优化了组合继承中的 new Parent(),避免了父类构造函数的重复调用。

优点:

  1. 避免引用属性共享问题
  2. 支持传参
  3. 方法可以复用
  4. 避免了 new Parent() 造成的二次调用

其中拓展一下,Object.create(Parent.prototype) 的原理

Object.create() 会创建一个新对象,并将其 __proto__ 指向参数对象:

Child 的实例能通过原型链访问 Parent.prototype 的方法(如 getName)。

但不会在 Child.prototype 上生成多余的父类属性(如 colors)。

Child.prototype = Object.create(Parent.prototype);
// 相当于:
Child.prototype = {};
Child.prototype.__proto__ = Parent.prototype;

例子:

function Parent(name) {
  this.name = name;
  this.colors = ["red", "blue", "green"];
}

Parent.prototype.getName = function() {
  return this.name;
};

function Child(name, age) {
  Parent.call(this, name); // 继承属性
  this.age = age;
}
// 实现原型方法继承。只继承原型方法,不调用构造函数
Child.prototype = Object.create(Parent.prototype); // 继承方法,但不会执行 Parent 构造函数
Child.prototype.constructor = Child; // 修正 constructor 指向

const child1 = new Child("Child1", 18);
console.log(child1.getName()); // Child1
console.log(child1.colors); // ["red", "blue", "green"]

5、ES6中class+extends 的继承

ES6中引入了class关键字,class可以通过extends关键字实现继承,还可以通过static关键字定义类的静态方法,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。

优点:

  1. 语法简洁,易读易写
  2. 继承逻辑清晰
  3. super()​​​​​​​ 关键字提供更清晰的继承机制

ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。

关键点

  • super(name) 等价于 Parent.call(this, name)(继承实例属性)。
  • extends 通过 Object.create() 实现原型方法的继承。

例子:

class Parent {
  constructor(name) {
    this.name = name;
    this.colors = ["red", "blue", "green"];
  }

  getName() {
    return this.name;
  }
}

class Child extends Parent {
  constructor(name, age) {
    super(name); // 调用父类构造函数,初始化子类的实例属性(相当于 Parent.call(this))
    this.age = age;
  }
}

const child1 = new Child("Child1", 18);
console.log(child1.getName()); // Child1
console.log(child1.colors); // ["red", "blue", "green"]

6、原型式继承(额外拓展一个)

原型式继承原型链继承构造函数继承 的折中方案,直接基于对象创建新对象。

  • 优点:不需要定义子类构造函数,已经原型链.prototype的指向操作。
  • 缺点:父类构造函数的变量值还是会共享,保持原型链继承的特征。

例子:

// Object.create()底层实现原理 (ES6的新特性)
      Object.create =  function (o) {
        var F = function () {};
        F.prototype = o;
        return new F();
      };

      let person = {
        name:'萨哈',
        things:[]
      }


      let a = Object.create(person)
      a.name = '小红'
      a.things.push('笔')
      console.log('a',a)

      let b = Object.create(person,{
        //定义该原型对象里每个变量的属性
        name:{
          //该变量的值
          value:'小黄',
          //该变量是否可写
          writable: false,
          //该变量是否能遍历
          enumerable:true,
          //是否允许外部操作修改这个变量的其它属性值(value属性除外)
          configurable:true
        }
      })
      b.name = '我要修改'
      console.log('b',b)

总结 

到此这篇关于JavaScript实现类继承方法的文章就介绍到这了,更多相关JS实现类继承内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Bootstrap入门书籍之(四)菜单、按钮及导航

    Bootstrap入门书籍之(四)菜单、按钮及导航

    这篇文章主要介绍了Bootstrap入门书籍之(四)菜单、按钮及导航的相关资料,需要的朋友可以参考下
    2016-02-02
  • 解决OneThink中无法异步提交kindeditor文本框中修改后的内容方法

    解决OneThink中无法异步提交kindeditor文本框中修改后的内容方法

    下面小编就为大家带来一篇解决OneThink中无法异步提交kindeditor文本框中修改后的内容方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-05-05
  • JavaScript简单实现合并两个Json对象的方法示例

    JavaScript简单实现合并两个Json对象的方法示例

    这篇文章主要介绍了JavaScript简单实现合并两个Json对象的方法,结合实例形式分析了json对象的遍历、添加实现合并的相关操作技巧,需要的朋友可以参考下
    2017-10-10
  • js简单实现交换Li的值

    js简单实现交换Li的值

    这篇文章主要介绍的是通过js简单实现交换Li的值,需要的朋友可以参考下
    2014-05-05
  • JavaScript中的DSL元编程介绍

    JavaScript中的DSL元编程介绍

    这篇文章主要介绍了JavaScript中的DSL元编程介绍,本文讲解了JavaScript元编程、JavaScript eval、JavaScript new Function()等内容,需要的朋友可以参考下
    2015-03-03
  • 基于JavaScript实现瀑布流布局(二)

    基于JavaScript实现瀑布流布局(二)

    这篇文章主要介绍了原生JavaScript实现瀑布流布局的相关资料,实现鼠标下拉图片自动加载效果,和百度图片效果类似,需要的朋友可以参考下
    2016-01-01
  • jJavaScript中toFixed()和正则表达式的坑

    jJavaScript中toFixed()和正则表达式的坑

    这篇文章主要介绍了jJavaScript中toFixed()和正则表达式的坑,toFixed方法可以把Number四舍五入为指定小数位数的数字,具体详细内容需要的小伙伴可以参考一下
    2022-04-04
  • webpack处理 css\less\sass 样式的方法

    webpack处理 css\less\sass 样式的方法

    本篇文章主要介绍了webpack处理 css\less\sass 样式的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • JS中filter( )数组过滤器的使用

    JS中filter( )数组过滤器的使用

    这篇文章主要介绍了filter() 数组过滤的使用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-11-11
  • 前端实现界面元素拖拽功能的3种方式总结(亲测有效)

    前端实现界面元素拖拽功能的3种方式总结(亲测有效)

    这篇文章主要介绍了前端实现界面元素拖拽功能的3种方式,三种方法分别是纯HTML+CSS+JS、Vue模板和Vue全局指令,每种方法都通过监听鼠标事件来实现元素的拖动功能,并通过控制阀来确保只有在指定区域按下鼠标时才开始拖动,需要的朋友可以参考下
    2025-02-02

最新评论