5种实现继承方式

一、原型链继承 (很少用)

原理:让子类构造函数的原型指向父类型构造函数的一个实例

// 定义Animal构造函数
    function Animal(color) {
        this.color = color;
        this.eat = ['肉']; // 引用类型属性
    }
    Animal.prototype.eating = function () {
        console.log("我能吃"+this.eat);
    };
    // 定义猫咪构造函数
    function Cat(name) {
        this.name = name;
    }
    // Cat原型指向Animal的一个实例!!!
    /**************重点语句**********/
    Cat.prototype = new Animal('yellow');
    /********************************/
    // 实例化Cat
    var c1 = new Cat('咪咪');
    var c2 = new Cat('泡泡');
    c1.color = 'black';
    c2.color = 'white'
    c1.eat.push('猫薄荷');
    c1.eating(); //我能吃肉,猫薄荷
    console.log(c1.color); //black
    console.log(c2.color); //white
    console.log(c1.constructor); // Animal函数体,不再是Cat了
    // 存在问题
    console.log(c1.eat); // 肉,猫薄荷
    console.log(c2.eat); // 肉,猫薄荷  受到实例c1的影响

存在问题:父构造函数中的属性都会成为共享属性,当父构造函数中有引用类型属性时,实例之间会相互影响

二、借用构造函数(伪造对象)(很少用)

原理:在子类型构造函数中执行父类构造函数,并将父类构造函数的this指向子类的new出来的对象上

// 定义Animal构造函数
    function Animal(color) {
        this.color = color;
        this.eat = ['肉']; // 引用类型属性
    }
    Animal.prototype.eating = function () {
        console.log(this.eat);
    }
    // 定义猫咪构造函数
    function Cat(name) {
        this.name = name;
        // 继承Animal 将Animal绑定到this对象上并执行!!!
        /**************重点语句**********/
        Animal.call(this,'red');
        /*******************************/
    }

    // 实例化Cat
    var c1 = new Cat('咪咪');
    var c2 = new Cat('泡泡');
    c1.color = 'black';
    c2.color = 'white'
    c1.eat.push('猫薄荷');
    c1.eating();  // 报错 因为父类原型的方法并没有继承到
    c2.eating();  // 报错 因为父类原型的方法并没有继承到
    console.log(c1.color); //black
    console.log(c2.color); //white
    console.log(c1.constructor); // Cat函数体
    console.log(c1.eat); // 肉,猫薄荷
    console.log(c2.eat); // 肉   不受实例c1的影响

解决问题:父类有引用类型对象的属性时,继承后实例间也不会相互影响

存在问题:1、父类中相同的方法也需要创建多次,浪费内存 2、子类访问不到父类原型的属性和方法了。

三、组合继承(常用的继承方式)

(组合原型链继承和借用构造两种方式)

// 定义Animal构造函数
    function Animal(color) {
        this.color = color;
        this.eat = ['肉']; // 引用类型属性
    }
    Animal.prototype.eating = function () {
        console.log("我能吃"+this.eat);
    };
    // 定义猫咪构造函数
    function Cat(name) {
        this.name = name;
        // 继承Animal 将Animal绑定到this对象上并执行!!!
        /**************重点语句1**********/
        Animal.call(this,'red');
        /*******************************/
    }
    // Cat原型指向Animal的一个实例!!!
    /**************重点语句2**********/
    Cat.prototype = new Animal('yellow');
    /*******************************/
    // 实例化Cat
    var c1 = new Cat('咪咪');
    var c2 = new Cat('泡泡');
    c1.color = 'black';
    c2.color = 'white'
    c1.eat.push('猫薄荷');
    c1.eating(); //我能吃肉,猫薄荷
    console.log(c1.color); //black
    console.log(c2.color); //white
    console.log(c1.constructor); // Animal函数体,不再是Cat了
    // 存在问题
    console.log(c1.eat); // 肉,猫薄荷
    console.log(c2.eat); // 肉  不受实例c1的影响

这种方式解决了原型链继承和借用构造函数继承两种方式带来的问题,融合了两种方式的优点,目前最常用的方式

存在一个小问题,此时子类的constructor不再是自身构造函数了

四、经典继承(规范化的原生式继承)

原理:利用Object.create()

// 定义一个字面量对象
    var animal = {
        name: '动物',
        color: 'red',
        eat: ['肉'],
        eating: function () {
            console.log('我能吃'+this.eat);
        }
    };
    /*************重点语句**********/
    var cat = Object.create(animal, {
        name: {
            value: "猫咪"
        }  // 重写属性会覆盖继承来的name属性,修改的是自身的name,不是animal的
    });
    cat.name = '小猫咪';  //这样修改不了自身的name,还是猫咪

    cat.eat.push('猫薄荷');
    cat.color = 'yellow';
    var dog = Object.create(animal); // 也可以省略第二个参数
    console.log(cat.name);  // 猫咪
    console.log(dog.name);  // 动物
    console.log(cat.eat);   // ‘肉’ ‘猫薄荷’
    console.log(dog.eat);   // ‘肉’ ‘猫薄荷’
    console.log(cat.color);   // yellow
    console.log(dog.color);   // red
    cat.eating();  // 我能吃‘肉’ ‘猫薄荷’
    dog.eating();  // 我能吃‘肉’ ‘猫薄荷’

优点:不需要兴师动众使用构造函数

存在问题:对象中有引用类型值属性时,实例间会相互影响

五、寄生组合式继承

在组合继承的前提下,子类原型指向父类原型的一个副本而不是父类的实例

 // 定义Animal构造函数
    function Animal(color) {
        this.color = color;
        this.eat = ['肉']; // 引用类型属性
    }
    Animal.prototype.eating = function () {
        console.log("我能吃"+this.eat);
    };
    // 定义猫咪构造函数
    function Cat(name) {
        this.name = name;
        // 继承Animal 将Animal绑定到this对象上并执行!!!
        /**************重点语句1**********/
        Animal.call(this,'red');
        /*******************************/
    }
    /**********重点语句2*********/

    function inheritPrototype(son, father) {
        var prototype = Object(father.prototype); // 将父类的原型创建一个副本
        prototype.constructor = son; //给副本指定constructor为子类构造函数
        son.prototype = prototype; //将子类原型指向这个副本
    }

    inheritPrototype(Cat,Animal);
    /****************************/
    // 实例化Cat
    var c1 = new Cat('咪咪');
    var c2 = new Cat('泡泡');
    c1.color = 'black';
    c2.color = 'white'
    c1.eat.push('猫薄荷');
    c1.eating(); //我能吃肉,猫薄荷
    console.log(c1.color); //black
    console.log(c2.color); //white
    console.log(c1.constructor); // cat函数体,
    console.log(c1.eat); // 肉,猫薄荷
    console.log(c2.eat); // 肉  不受实例c1的影响

优化了组合继承(子类.construtor指向问题)的小问题,是最理想的继承了

原文地址:https://www.cnblogs.com/mengjingmei/p/9385042.html