干了这杯酱香文,一起搞懂js的继承

57 阅读4分钟

在 JavaScript 中,有几种方式可以实现继承。以下是常用的几种方式:

image-20230926135014763.png

1.构造函数本质是函数,使用new关键字来创建对象,所有函数都是Function()的实例

2.原型对象是用来存放实例对象的公有属性和公有方法的一个公共对象

3.原型链又叫隐式原型链,是由__proto__属性串联起来,原型链的尽头是Object.prototype

  1. 原型链继承(Prototype Inheritance):

    • 通过将子类的原型对象指向父类的实例来实现继承。
    • 通过 Child.prototype = new Parent() 来设置子类的原型。
    • 缺点是所有子类实例共享同一个父类实例。
    //酱香案例
    function maoTai(name) {
      this.name = name;
    }
    
    maoTai.prototype.drink = function() {
      console.log("入口柔,一线喉!");
    };
    
    // 创建 latte 类
    function latte(name) {
      this.name = name;
    }
    
    // 核心代码,继承 maoTai 类
    latte.prototype = new maoTai();
    
    // 添加 bark 方法到 latte 类
    latte.prototype.latteDrink = function() {
      console.log("新中式拿铁,好喝,不上头");
    };
    
    const myDrink = new latte("酱香拿铁");
    myDrink.drink(); // 输出: 入口柔,一线喉!
    myDrink.latteDrink(); // 输出: 新中式拿铁,好喝,不上头
    console.log(myDrink.name); // 输出: 酱香拿铁
    
  2. 构造函数继承(Constructor Inheritance):

    • 在子类构造函数中调用父类构造函数,使用 callapply 方法将父类的属性和方法添加到子类实例上。
    • 通过 Parent.call(this) 来调用父类构造函数。
    • 缺点是无法继承父类原型上的方法。
    //酱香案例
    function maoTai(name) {
      this.name = name;
    }
    
    maoTai.prototype.drink = function() {
      console.log("入口柔,一线喉!");
    };
    
    function latte(name) {
      // 这里进行构造函数继承
      // 使用 maoTai.call(this, name) 调用 maoTai 构造函数,并将当前对象(即 latte 实例)作为上下文
      maoTai.call(this, name);
    }
    
    const myDrink = new latte("酱香拿铁");
    console.log(myDrink.name); // 输出: 酱香拿铁
    myDrink.drink(); // 输出: 报错,myDrink.drink is not a function
    
    
  3. 组合继承(Combination Inheritance):

    • 结合了原型链继承和构造函数继承的方式。
    • 通过将子类的原型对象指向父类的实例,同时在子类构造函数中调用父类构造函数,实现对实例属性和原型方法的继承。
    • 缺点是会调用两次父类构造函数,一次在设置原型时,一次在创建子类实例时。
    //酱香案例
    function maoTai(name) {
      this.name = name;
    }
    
    maoTai.prototype.drink = function() {
      console.log("入口柔,一线喉!");
    };
    
    function latte(name) {
      maoTai.call(this, name);// 这里进行构造函数继承,继承属性
    }
    latte.prototype = new maoTai();// 这里进行原型链继承,继承方法
    
    latte.prototype.latteDrink = function() {
      console.log("新中式拿铁,好喝,不上头");
    };
    
    const myDrink = new latte("酱香拿铁");
    console.log(myDrink.name); // 输出: 酱香拿铁
    myDrink.drink(); // 输出: 入口柔,一线喉!
    myDrink.latteDrink(); // 输出: 新中式拿铁,好喝,不上头
    
  4. 原型式继承(Prototypal Inheritance):

    • 使用一个中间对象作为桥梁,通过浅拷贝父对象的属性来创建一个新对象。
    • 使用 Object.create(obj) 来创建一个以 obj 为原型的新对象。

image-20230926171946044.png

    //酱香案例
    const maoTai = {
      name:"茅台",
      drink: function() {
        console.log("入口柔,一线喉!");
      }
    };

    const latte = Object.create(maoTai);

    latte.latteDrink = function() {
      console.log("新中式拿铁,好喝,不上头");
    };

    latte.name = "酱香拿铁"
    latte.getName = function() {
      console.log(`我是${this.name}`);
    };

    latte.drink(); // 输出: 入口柔,一线喉!
    latte.latteDrink(); // 输出: 新中式拿铁,好喝,不上头
    latte.getName(); // 输出: 我是酱香拿铁
    ```

5.  寄生式继承(Parasitic Inheritance):

    *   在原型式继承的基础上,增强对象,添加新属性或方法,返回增强后的对象。
    *   通过在一个函数内部创建一个新对象,并对其进行增强,最后返回这个新对象。

    ```javascript
    //酱香案例
    const maoTai = {
      drink: function() {
        console.log("入口柔,一线喉!");
      }
    };

    function createLatte(name) {
      const latte = Object.create(maoTai); // 通过调用函数创建一个新对象
      latte.name = name;
      latte.latteDrink = function() {
      console.log("新中式拿铁,好喝,不上头");
    };// 以某种方式增强这个对象
      return latte;// 返回这个对象
    }

    const myDrink = createLatte("酱香拿铁");

    myDrink.drink(); // 输出: 入口柔,一线喉!
    console.log(myDrink.name); // 输出: 酱香拿铁
    myDrink.latteDrink(); // 输出: 新中式拿铁,好喝,不上头
    ```

6.  寄生组合式继承(Parasitic Combination Inheritance):
    *   结合组合继承和寄生式继承的方式。
    *   通过寄生式继承来继承父类的原型,然后将结果赋值给子类的原型。
    *   通过 `Child.prototype = Object.create(Parent.prototype)` 实现原型的继承。
    *   ![image-20230927111622931转存失败,建议直接上传图片文件](C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20230927111622931.png)
    ```javascript
    //酱香案例
    function maoTai(name) {
      this.name = name;
    }

    maoTai.prototype.drink = function() {
      console.log("入口柔,一线喉!");
    };

    function latte(name) {
      // 继承属性
      maoTai.call(this, name);
    }

    function inheritPrototype(subType, superType) { 
     let prototype = Object.create(superType.prototype); // 创建对象
     prototype.constructor = subType; // 增强对象 
     subType.prototype = prototype; // 赋值对象
    } 

    inheritPrototype(latte,maoTai)

    latte.prototype.latteDrink = function() {
      console.log("新中式拿铁,好喝,不上头");
    };

    const myDrink = new latte("酱香拿铁");

    console.log(myDrink.name); // 输出: 酱香拿铁
    myDrink.drink(); // 输出: 入口柔,一线喉!
    myDrink.latteDrink(); // 输出: 新中式拿铁,好喝,不上头
    ```

7.  ES6 里的 extends 的语法糖

```javascript
//酱香案例
class MaoTai {
    constructor (name, type) {
        this.name = name
        this.type = type
    }
    drink () {
        console.log("入口柔,一线喉!")
    }
}

class latte extends MaoTai {
    constructor (name, type, IceCubes) {
        super(name, type)
        this.IceCubes = IceCubes
    }
    latteDrink () {
        console.log("新中式拿铁,好喝,不上头")
    }
}

const myLatte = new latte("酱香拿铁","咖啡",5)
console.log(myLatte)