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.
相关推荐
EndingCoder1 小时前
调试技巧:Chrome DevTools 与 Node.js Inspector
javascript·网络·electron·node.js·vim·chrome devtools
知识分享小能手1 小时前
React学习教程,从入门到精通, React 入门指南:React JSX 语法知识点详解及案例代码(8)
前端·javascript·vue.js·学习·react.js·前端框架·anti-design-vue
学习3人组1 小时前
React 组件基础与事件处理
前端·javascript·react.js
qczg_wxg6 小时前
React Native的动画系统
javascript·react native·react.js
漂流瓶jz7 小时前
解锁Babel核心功能:从转义语法到插件开发
前端·javascript·typescript
大怪v7 小时前
老乡,别走!Javascript隐藏功能你知道吗?
前端·javascript·代码规范
ERP老兵-冷溪虎山8 小时前
Python/JS/Go/Java同步学习(第三篇)四语言“切片“对照表: 财务“小南“纸切片术切凭证到崩溃(附源码/截图/参数表/避坑指南/老板沉默术)
java·javascript·python·golang·中医编程·四语言同步学习·职场生存指南
gnip8 小时前
结合Worker通知应用更新
前端·javascript
_Rookie._9 小时前
vue3 使用css变量
前端·javascript·html
良木林11 小时前
JS函数进阶
开发语言·前端·javascript