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.
相关推荐
mCell1 小时前
JavaScript 运行机制详解:再谈 Event Loop
前端·javascript·浏览器
amy_jork3 小时前
npm删除包
开发语言·javascript·ecmascript
独行soc4 小时前
2025年渗透测试面试题总结-18(题目+回答)
android·python·科技·面试·职场和发展·渗透测试
艾伦~耶格尔4 小时前
【数据结构进阶】
java·开发语言·数据结构·学习·面试
max5006005 小时前
基于桥梁三维模型的无人机检测路径规划系统设计与实现
前端·javascript·python·算法·无人机·easyui
我命由我123456 小时前
软件开发 - 避免过多的 if-else 语句(使用策略模式、使用映射表、使用枚举、使用函数式编程)
java·开发语言·javascript·设计模式·java-ee·策略模式·js
愿天堂没有C++6 小时前
剑指offer第2版——面试题4:二维数组中的查找
c++·面试
萌萌哒草头将军6 小时前
Node.js v24.6.0 新功能速览 🚀🚀🚀
前端·javascript·node.js
AALoveTouch7 小时前
大麦APP抢票揭秘
javascript
持久的棒棒君7 小时前
启动electron桌面项目控制台输出中文时乱码解决
前端·javascript·electron