JS常见的五种继承方式

一、原型链继承

特点: 实例可继承的属性有:实例的构造函数的属性,父类构造函数属性,父类原型的属性。(新实例不会继承父类实例的属性!)

将子类的原型指向父类构造函数的实例对象

修改简单数据类型不会造成数据共享,修改引用类型是会造成child实例之间的数据共享

js 复制代码
 function Parent() {
      this.name = 'abc';
      this.info = {
        weight: 180,
        height: 180
      }
    }

    function Child() {
      this.age = 12
    }

    const parent1 = new Parent()
    Child.prototype = parent1  //将子类的原型指向了父类构造函数的实例对象
     
    const child1 = new Child()
    const child2 = new Child()
    child1.name = 'xym'
    child2.info.height = 100
    console.log(child1)
    console.log('---')
    console.log(child2);

缺点: 1、新实例无法向父类构造函数传参。

2、所有新实例都会共享父类实例的引用类型属性。当原型上有一个引用类型的属性时,所有实例都共享的是该引用类型属性的 同一份引用

二、借用构造函数继承

js 复制代码
    function Parent() {
      this.name = 'abc';
      this.info = ['hehe','haha']
    }
    
    Parent.prototype.say = function (){
      console.log('111');  //缺点:无法继承父类原型上的方法
    }

    function Child() {
      this.age = 12
      Parent.call(this)   //在子类构造函数中调用父类构造函数,父类中的this绑定到了子类实例上
    }    
     
    const child1 = new Child()
    const child2 = new Child()
    child1.info = ['aaa']
    
    console.log(child1)
    console.log('---')
    console.log(child2);
    console.log(new Parent())

重点:.call()和.apply() 将父类构造函数引入子类函数(在子类函数中做了父类函数的自执行(复 制))

特点:

1、解决了原型链继承引用属性共享的问题。

2、可以继承多个构造函数属性(call 多个)。

js 复制代码
function Father() {
  this.fromFather = 'father';
}
function Mother() {
  this.fromMother = 'mother';
}

function Child() {
  Father.call(this);
  Mother.call(this);
}

3 、在子实例中可向父实例传参。

js 复制代码
    function Parent (age){
      this.age = age 
    }

    function Son (name, age){
      this.name = name
      Parent.call(this, age)    //可以在调用父构造函数时传参
    }

    const son1 = new Son('xym', 21)
    console.log(son1);

缺点: 1、只能继承父类构造函数的属性,不能继承原型上的方法

2、无法实现构造函数的复用。(每次用每次都要重新调用)

三、组合继承(组合原型链继承和借用构造函数继承)

js 复制代码
 function Parent (){
      this.name = ['aaa','bbb']
    }

    function Child (){
      this.age = 21
      Parent.call(this)    //第二次调用Parent
    }
    
    Child.prototype = new Parent()   // 第一次调用Parent

    var child1 = new Child() 
    var child2 = new Child() 
    child1.name = ['aaa'] //为什么解决了共享属性的问题?在第二次调用Parent
                          //时,this.name覆盖掉了第一次用原型链继承的this.name 
    console.log(child1, child2);

重点: 结合了两种模式的优点,传参和复用

缺点: 调用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数。

四、寄生组合继承

js 复制代码
 function Parent(){
      this.name = ['aaa','bbb']
    }

    function Child(){
      Parent.call(this)
    }

    Parent.prototype.sing = function (){ console.log('111');
    }
    Child.prototype.dance = function (){ console.log('222');
    }
    
    //Object.create()为ES5新语法,如果不兼容,就自己手写实现
    if(!Object.create){
      Object.create = function(proto){
        function F (){}
        F.prototype = proto     //F的prototype赋值为proto对象
        return new F()    // new 创建一个新实例对象,将这个对象的原型(__proto__)指向proto对象
      }
    }

    Child.prototype = Object.create(Parent.prototype)
    //  Object.create() 创建一个空对象
    // 将空对象的__proto__(原型)指向Parent.prototype
    // 将新对象赋值给 Child.prototype
    // 缺点:这里Child.prototype 重新赋值会把原本Child原型上的方法覆盖掉
    
    console.log(new Child());   这个缺点该怎么解决???

解决方案:

js 复制代码
 function Parent() {
        this.name = ['aaa', 'bbb'];
      }

      function Child() {
        Parent.call(this);
      }

      Parent.prototype.sing = function () { console.log('111'); };

      // 先建立继承关系
      Child.prototype = Object.create(Parent.prototype);

      // 再添加子类方法
        Child.prototype.dance = function () { console.log('222'); };

      // 修复constructor指向
      Child.prototype.constructor = Child;

      console.log(new Child());
      // 现在可以访问:
      new Child().dance() 

五、class 类实现继承

通过 extends 实现继承, super调用父类构造函数。子类实例可以直接调用父类的方法,子类class内部还可以直接调用父类方法

js 复制代码
// 父类
class ParentClass {
    constructor(name) {
        this.name = name;
    }

    // 父类的一个方法
    greet() {
        console.log(`Hello from ParentClass! My name is ${this.name}.`);
    }
}

class ChildClass extends ParentClass {
    constructor(name, age) {
        super(name); //继承属性
        this.age = age;
    }

    // 子类自己的方法
    introduce() {
        console.log(`I am ${this.name} and I am ${this.age} years old.`);
        this.greet()
    }
}

const child = new ChildClass("Alice", 5);

// 子类实例可以直接调用父类的方法
child.greet();      // 输出: Hello from ParentClass! My name is Alice.
child.introduce();  // 输出: I am Alice and I am 5 years old.
相关推荐
不爱吃糖的程序媛13 分钟前
Electron 如何判断运行平台是鸿蒙系统(OpenHarmony)
javascript·electron·harmonyos
Hilaku29 分钟前
我用AI重构了一段500行的屎山代码,这是我的Prompt和思考过程
前端·javascript·架构
Cxiaomu30 分钟前
React Native App 自动检测版本更新完整实现指南
javascript·react native·react.js
007php0071 小时前
大厂深度面试相关文章:深入探讨底层原理与高性能优化
java·开发语言·git·python·面试·职场和发展·性能优化
掘金安东尼1 小时前
前端周刊第439期(2025年11月3日–11月9日)
前端·javascript·vue.js
前端一课2 小时前
2025年-vue3面试题(AI分析详细版)
面试
起这个名字2 小时前
微前端应用通信使用和原理
前端·javascript·vue.js
用户90443816324602 小时前
AI 生成的 ES2024 代码 90% 有坑!3 个底层陷阱 + 避坑工具,项目 / 面试双救命
前端·面试
鹏多多2 小时前
Web使用natapp进行内网穿透和预览本地页面
前端·javascript
钱端工程师3 小时前
uniapp封装uni.request请求,实现重复接口请求中断上次请求(防抖)
前端·javascript·uni-app