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实现类继承内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • JavaScript中fromCharCode 和 fromCodePoint 的详解与应用小结

    JavaScript中fromCharCode 和 fromCodePoint 的详解与应

    本文将详细介绍 JavaScript 中的 String.fromCharCode 和 String.fromCodePoint 方法,这两个方法能够帮助开发者高效地处理字符与编码之间的转换,理解它们的区别与应用,将让你的代码更加灵活和高效,感兴趣的朋友跟随小编一起看看吧
    2024-12-12
  • H5图片压缩与上传实例

    H5图片压缩与上传实例

    这篇文章主要为大家详细介绍了H5图片压缩与上传的实例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-04-04
  • JavaScript实现滚动加载更多

    JavaScript实现滚动加载更多

    这篇文章主要为大家详细介绍了JavaScript实现滚动加载更多,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-12-12
  • 浅析JavaScript 函数柯里化

    浅析JavaScript 函数柯里化

    这篇文章主要介绍了JavaScript 函数柯里化的相关资料,帮助大家更好的理解和学习JavaScript,感兴趣的朋友可以了解下
    2020-09-09
  • bootstrapValidator自定验证方法写法

    bootstrapValidator自定验证方法写法

    这篇文章主要为大家详细介绍了bootstrapValidator自定验证方法写法,研究bootstrapValidator验证,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-12-12
  • js实现图片切换(动画版)

    js实现图片切换(动画版)

    本文主要对javascript实现图片切换(动画版)的方法进行步骤分析、实例介绍,具有很好的参考价值,需要的朋友一起来看下吧
    2016-12-12
  • 基于JavaScript实现动态雨滴特效

    基于JavaScript实现动态雨滴特效

    这篇文章主要为大家详细介绍了如何利用JavaScript实现动态雨滴特效并且点击雨滴会有雨滴爆裂的效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • 微信接入之获取用户头像的方法步骤

    微信接入之获取用户头像的方法步骤

    这篇文章主要介绍了微信接入之获取用户头像的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-09-09
  • 让FireFox支持innerText的实现代码

    让FireFox支持innerText的实现代码

    DHTML非标准属性innerText在FireFox中的使用
    2009-12-12
  • JS中数组合并的几种常见方法

    JS中数组合并的几种常见方法

    这篇文章主要给大家介绍了关于JS中数组合并的几种常见方法,在开发的过程中,我们很多时候会遇到需要将两个数组合并成一个数组的情况,文中通过实例代码介绍的非常详细,出现需要的朋友可以参考下
    2023-07-07

最新评论