关于JavaScript的继承

596 阅读2分钟

1. 类式继承

首先我们从原型中来看

function Animal(){ }
let a1 = new Animal()

// 每一个对象属性,其隐式原型全部指向其父级的prototype原型 
// 因为Animal是通过function构造出来的,所以其隐式原型指向Function的原型
console.log(Animal.__proto__ === Function.prototype);    //true
// 根据Function原型和Object对象的原型关系,我们可以得到 这样一层关系
console.log(Animal.prototype.__proto__ === Object.prototype);   // true
// 之后可以的到任何一个实例化的对象 其隐式原型(__proto__)都是指向其父级的原型(prototype)的
console.log(a1.__proto__ === Animal.prototype);   //true
console.log(a1 instanceof Object);   //true
console.log(a1.__proto__ === Object.protortpe)   // false

// 基本结论: 任何一个实例化的对象其隐式原型(__proto__)都指向其父级的原型(protorypr)
// 为了证实一下
let temp = {}
console.lot(temp.__proto__ === Object.prototype)  //true

// 原型链的产生,以及第一种方式继承的出现
console.log(a1.__proto__.__proto__ === Object.prototype);   // true

第一种继承方式 :让子对象的原型定向为一个父级的实例,这样子类的__proto__就指向了父类

function Animal (name){
    this.name = 'animal' 
    this.say = function(){
      console.log(this.name);
    }
    this.setName = function(name){
      this.name = name
    }
}

function Cat(name){      }

Cat.prototype = new Animal()

const RagaMuffin = new Cat()
RagaMuffin.say()      // animal
RagaMuffin.setName('褴褛猫')
RagaMuffin.say()      // 褴褛猫
console.log(RagaMuffin);
// 缺点: 父级属性共享,当修改父级属性会影响到其他实例
RagaMuffin.__proto__.name = '褴褛猫'

const Tabby = new Cat()
Tabby.say()           // 褴褛猫
Tabby.setName("虎斑猫")
Tabby.say()           // 虎斑猫
console.log(Tabby);

优点:

  1. 直观简便的继承了父级的方法 缺点:
  2. 无法接收子类的动态参数
  3. 父级属性共享

image.png

2.构造函数继承

function Animal (name){
    this.name = name 
    this.say = function(){
      console.log(this.name);
    }
}
// 对原型添加方法
Animal.prototype.eat = function(){
    console.log(this.name , '吃鱼');
}
// 创建子类
function Cat(name){    
    Animal.call(this, name)
}

const RagaMuffin = new Cat('褴褛猫')
RagaMuffin.say()      // 褴褛猫
RagaMuffin.eat()      // RagaMuffin.eat is not a function

image.png

缺点:

  1. 构造函数继承没能继承父级原型上的方法

3. 组合继承

结合上述两种继承方式的方法叫做组合继承

  function Animal (name){
    this.name = name 
    this.say = function(){
      console.log(this.name);
    }
    Animal.prototype.eat = function(){
      console.log(this.name , '吃鱼');
    }
  }

  function Cat(name){    
    Animal.call(this, name)
  }

  Cat.prototype = new Animal()
  const RagaMuffin = new Cat('褴褛猫')
  console.loe(RageMyffin)   
  RagaMuffin.say()      // 褴褛猫
  RagaMuffin.eat()      // 褴褛猫 吃鱼

缺点:

  1. 子类无法传递动态参数给父类
  2. 父类的构造函数调用了两次 image.png

4. 原型式继承

function createObject(o) {
    // 创建临时类
    function f() {
    }
    // 修改类的原型为o, 于是f的实例都将继承o上的方法
    f.prototype = o
    return new f()
}

const obj = {
name: 'chang'
}
const chang = createObject(obj)
console.log(chang.name);        // chang

缺点: 共享了属性和方法,没有解决类式继承的缺点

5.寄生继承

创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象

function createAnother(original) {
  var clone = Object.create(original);    //通过调用函数创建一个新对象
  clone.say = function () {               //以某种方式来增强这个对象
    console.log('Hello');
  };
  return clone;                        //返回这个对象
}

const obj = {
  name: 'chang',
  testArr: [1, 2, 3]
}

const newObj = createAnother(obj)
console.log(newObj.name);     // chang
newObj.say()                  // Hello
newObj.testArr.push(4)
console.log(newObj.testArr);  //(4) [1, 2, 3, 4]

6. 寄生组合继承

function inherit(child, parent) {
      // 获取父级的原型
      const parent_proto = Object.create(parent.prototype)
      // 子类继承父类的原型
      child.prototype = parent_proto
      // 父类的构造函数指向child,防止污染
      parent_proto.constructor = child
    }

    // 父类
    function Animal(name) {
      this.name = name
      this.say = function () {
        console.log(this.name);
      }
      Animal.prototype.eat = function () {
        console.log(this.name, '吃鱼');
      }
    }


    // 子类
    function Cat(name) {
      Animal.call(this, name)
    }

    inherit(Cat, Animal)


    const RagaMuffin = new Cat('褴褛猫')
    console.log(RagaMuffin);
    RagaMuffin.say()      // 褴褛猫
    RagaMuffin.eat()

1)RDR7TG3MQZQ`6$H2Y95N8.png