ES5继承方式总结

118

前言

ES5的继承方式有多种。很多的继承方式也会存在一些问题,下面我将对ES5中常见的几种继承方式进行一些阐述。

本文总结的继承有:

  • 冒充继承(构造函数继承)
  • 原型式继承
  • 组合式继承
  • 寄生式继承

一、 冒充继承(构造函数继承)

原理:调用父类构造函数,并改变其中的this, 通过bind、call、apply来改变this。

这种继承方式有局限性,“父类”原型上的属性方法无法继承。下面从代码角度看一下是怎么实现的。

 // 构造函数继承(对象冒充继承)
function Cat(n,c){ // 猫 类
   this.name = n;
   this.color = c;
   this.trait = function (){
       console.log('卖萌');
   }
}
Cat.prototype.skill = function (){ // 原型上的属性方法
   console.log('抓老鼠');
}

// 需求:狗要卖萌,狗要多管闲事-抓老鼠
function Dog(n,c,f){ // 狗 类
   this.food = f;
   Cat.call(this,n,c); // 狗冒充猫,访问猫的属性方法
}
var dog1 = new Dog('二哈','yellow','shi');// 实例对象
console.log(dog1.name); // 二哈
dog1.trait(); // 卖萌
dog1.skill(); // “父类”原型上的属性方法无法继承,所以会报错 dog1.skill is not a function

二、原型式继承

原理:将原型对象链接到另一个对象实现继承(改变原型的指向)。

这种继承方式有局限性,实例化对象的时候不能给“父类”传参,并且其constructor指向的是继承对象的constructor。下面从代码角度看一下是怎么实现的。

// demo2 原型链继承
// 原理:将原型对象链接到另一个对象实现继承(改变原型的指向)
function Cat(n,c){ // 猫 类
   this.name = n;
   this.color = c;
   this.trait = function (){
       console.log('卖萌~');
   }
}
Cat.prototype.skill = function (){// 原型上的属性方法
   console.log('抓老鼠');
}

function Dog(n,c,f){ // 狗 类
   this.food = f;
}
Dog.prototype = new Cat(); // 把狗的原型指向猫的实例对象

var dog1 = new Dog('二哈','yellow','shi');
console.log(dog1.name); // undefined
console.log(dog1.food); // shi
dog1.trait(); // 卖萌~
dog1.skill(); // 抓老鼠
console.log(dog1.constructor); // Cat

三、组合式继承

这种方式是将上边的冒充继承和原型链继承结合起来。 组合继承解决原型链继承不能传参的问题,以及冒充继承不能继承原型对象的属性问题,但是组合继承还是存在问题:1、不能传递参数到原型对象,2、执行了两次构造函数

// 组合式继承
        function Ball(_a){
            //冒充继承
            Box.call(this,_a);
        }
        //原型式继承
        Ball.prototype=new Box();
        Ball.prototype.constructor=Ball;
        var b=new Ball(10);
        console.log(b);
        //两种方式结合可以实现相对比较完美的“继承”
        //别忘了指正构造器(类型)

四、寄生式继承

下面代码实现了寄生式继承,此方式解决了组合式继承存在的问题,是一种完美的继承方式。

        //寄生式继承
        function extend(subClass, supClass) {
            // 创建一个中间替代类,防止多次执行父类(超类)的构造函数
            function F() { }
            // 将父类的原型赋值给这个中间替代类
            F.prototype = supClass.prototype;
            // 将原子类的原型保存
            var proto = subClass.prototype;
            // 将子类的原型设置为中间替代类的实例对象
            subClass.prototype = new F();
            // 将原子类的原型复制到子类原型上,合并超类原型和子类原型的属性方法,只能复制可枚举属性
            //Object.assign(subClass.prototype,proto);
            var names = Object.getOwnPropertyNames(proto);
            for (var i = 0; i < names.length; i++) {
                var desc = Object.getOwnPropertyDescriptor(proto, names[i]);
                Object.defineProperty(subClass.prototype, names[i], desc);
            }
            // 设置子类的构造函数是自身的构造函数,以防止因为设置原型而覆盖构造函数(可以不用写因为已经复制了)
            subClass.prototype.constructor = subClass;
            // 给子类的原型中添加一个属性,可以快捷的调用到父类的原型方法
            subClass.prototype.superClass = supClass.prototype;
            // 如果父类的原型构造函数指向的不是父类构造函数,重新指向
            if (supClass.prototype.constructor !== supClass) {
                supClass.prototype.constructor = supClass;
            }
        }
        
        
        //创建Box构造函数
        function Box(_a) {
            console.log("aaa");
            //实例化对象中有a这个属性
            this.a = _a;
            //执行原型对象下的play方法
           this.play();
            
        }
        //设置原型对象的属性a
        Box.prototype.a = 10;
        //设置原型对象的选访问器属性(为了找到问题才加的)(为啥出现在了实例化对象中)
        Object.defineProperty(Box.prototype, "num", {
            set: function (value) {
                this._num = value;
            },
            get: function () {
                if (!this._num) this._num = 0;
                return this._num;
            }
        })
        Box.prototype.play = function () {
            console.log("play");
        }
        Box.b = 20;
        
        function Ball(_a) {
            this.superClass.constructor.call(this, _a);
        }
         Ball.prototype.play = function () {
            this.superClass.play.call(this);//执行超类的play方法
            console.log("end");
        }
        Object.defineProperty(Ball.prototype, "d", {
            value: 20
        }) 

         extend(Ball, Box);

        var b = new Ball(10);
        console.log(b);