JS 之 继承

馋奶兔 提交于 2020-01-19 03:58:11

一 、 原型链继承

父类型实例赋值给子类型原型

        function Animal(){
            this.value='animal';
        }
        //父类型的方法
        Animal.prototype.run=function(){
            return this.value+''+'is runing';
        }

        function Cat(){}
        //继承,用构造函数创建对象
        //通过new 将构造函数的作用域赋给新对象,链接到原型
        //绑定this,执行代码,添加属性,返回新对象
          //Cat.prototype.__proto__=Animal.prototype
        Cat.prototype=new Animal();//Cat.prototype 变成了 Animal 的一个实例,所以 Animal 的实例属性 names 就变成了 Cat.prototype 的属性。

        var instance=new Cat();
        instance.value='hellokitty';
        console.log(instance.run());//hellokitty is run

原型链如图

在这里插入图片描述

此方法有以下缺点:

  • 当原型链中包含引用类型值的原型时,该引用类型值会被所有实例共享
function Animal(){
  this.names = ["cat", "dog"];//属性包含数组,即引用类型值
}
function Cat(){}

Cat.prototype = new Animal();

var instance1 = new Cat();
instance1.names.push("tiger");
console.log(instance1.names); // ["cat", "dog", "tiger"]

var instance2 = new Cat(); 
console.log(instance2.names); // ["cat", "dog", "tiger"]
  • 子类型原型上的 constructor 属性被重写了,重写原型会失去默认constructor
        if (Cat.prototype.constructor===Animal){
            alert("true");//true
        }

在这里插入图片描述

  • 在创建子类型时,不能向超类型的构造函数中传递参数.

二 、借用构造函数(伪造对象OR经典方法)

在子类构造函数内部调用超类构造函数
用子类去创建实例时”借用”父类的构造函数

  • 核心代码是Animal.call(this),创建子类实例时调用Animall构造函数,于是Cat的每个实例都会将Animall中的属性复制一份,使用父类的构造函数来增强子类实例,等同于复制父类的实例给子类
        function Animal(){
            this.color=["yellow","white","black"];
        }

        function Cat(){
            Animal.call(this);//继承
        }

        var HelloKitty=new Cat();//cat相当于中间件,为了多复制超类
        HelloKitty.color.push("pink");
        console.log(HelloKitty.color);//[ "yellow", "white", "black", "pink" ]
  • 保证了原型链中引用类型值的独立, 避免了引用类型的属性被所有实例共享
        var HongMao=new Cat();
        HongMao.color.push("red");
        console.log(HongMao.color);//["yellow", "white", "black", "red" ]
  • 可以直接在Child中向Parent传参
        function Animal(gender){
            this.gender=gender;
        }

        function Cat(gender){
            Animal.call(this,"girl");//继承
            Cat.age=18;//实例属性
        }

        var HelloKitty=new Cat();
        console.log(HelloKitty.gender);//girl

缺点

  • 方法都在构造函数中定义,每次创建实例都会创建一遍方法,每个子类都有父类实例函数的副本,影响性能
  • 只能继承父类的实例属性和方法,不能继承原型属性/方法
  • 不使用原型,无法实现函数复用

三 、组合继承(伪经典继承)

原型链及借用构造函数的组合
原型链实现继承原型属性与方法,借用构造函数实现实例属性的继承

  • 定义父类属性及方法
        function Animal(size){
            this.size=size;
            this.color=["yellow","white","black"]
        }
        //为原型对象添加方法
        Animal.prototype.saysSize=function(){
            console.log(this.size);
        }
  • 借用构造函数实现实例属性的继承,可添加子类属性
  • 原型链实现继承原型属性与方法,可添加子类属性
    //第二次调用父类构造器
   
        function Cat(size ,gender){
            Animal.call(this,size);// 借用构造函数   继承实例属性并添加自己的属性
            this.gender=gender;
        }
    //第一次调用父类构造器
    //Cat有一个size和color
        Cat.prototype=new Animal();//原型链继承   方法
        Cat.prototype.constructor=Cat;//矫正构造函数

        Cat.prototype.sayGender=function(){//添加子类方法
        	console.log(this.gender);
        }
  • 实例
//实例化的时候,第二次调用Animal构造函数,会再创建一size和color
        var HelloKitty=new Cat("little","girl");
        HelloKitty.color.push("pink");
        console.log(HelloKitty.color);//[ "yellow", "white", "black", "pink" ]
        HelloKitty.sayGender();//girl
        HelloKitty.saysSize();//little

        var HongMao=new Cat("big","boy");
        HongMao.color.push("red");
        console.log(HongMao.color);//[ "yellow", "white", "black", "red" ]
        HongMao.sayGender();//bot
        HongMao.saysSize();//big

缺点

  • 使用子类创建实例对象时,其原型中会存在两份相同的属性/方法
  • 组合继承其实调用了两次父类构造函数, 造成了不必要的消耗
    在这里插入图片描述

四、 原型式继承

利用一个空对象作为中介,将某个对象直接赋值给空对象构造函数的原型。
基于原有对象创建新对象,用Animal对象创建新对象。不用写function Animal()
{}
一个对象继承另一个对象的属性可用

  • 原型式函数
    实际是创建o.construct对应的function o(){}
        function object(o){
            function fn(){};//创建临时构造函数
            fn.prototype=o;//关系创建,构造函数的原型对象是o
            return new fn();//返回构造函数的实例
        }

在这里插入图片描述

  • 构造函数原型对象
        var Animal={
            value:animal,
            size:["big","small"]  
        };
  • 继承
        var Cat=object(Animal);//==new Animal();
        Cat.name="HelloKitty";//重写属性
        Cat.size.push("mid");//添加引用类型值

        console.log(Cat.name);//hellokitty
        console.log(Cat.size) //[ "big", "small", "mid" ]
        console.log(Animal.name);//[ "big", "small", "mid" ]
        console.log(Animal.size);//animal
  • ES5的另一种写法,使用Object.creat()方法
        var Animal={
            name:"animal",
            size:["big","small"]
        };
        var Dog=Object.create(Animal);//==object(Animal),自带方法
        Dog.name="Snoopy";//重写属性
        Dog.size.push("little");//添加引用类型值
        console.log(Dog.name);//Snoopy
        console.log(Dog.size) //[ "big", "small", "mid" ,"litttle"]

缺点

引用类型值会被共享

寄生式继承

一个对象对另一个对象的继承,
改进的原型式 ,比原型式继承多了函数继承

  • 创建一个封装继承过程的函数,内部可以创建函数等方式来增强对象,这一步也可使用Object.creat()
		var Animal={
            name:"animal",
            size:["big","small"]
        };
        function object(o){
	        function fn(){};//创建临时构造函数
	        fn.prototype=o;//关系创建,构造函数的原型对象是o
	        return new fn();//返回构造函数的实例
	    }
  • 创建一个”假“新对象,使用原型式方法,再添加函数。返回“假”新对象
        function creat(ori){
            var clone=object(ori);
            clone.SayName=function(){
                console.log(clone.name);
            };
            return clone//返回值
        }
  • 通过封装函数将“假”新对象赋给新对象(子类)
        var Cat=creat(Animal);//将clone赋给Cat
        console.log(Cat.size);// [ "big", "small" ]
        Cat.name="HelloKitty";
        Cat.SayName();//HelloKitty

缺点

  • 和构造函数模式一样,无法函数复用 和原型式一样,无法传递参数和引用类型值实例共享

寄生组合式继承

寄生组合式是引用类型最理想的继承方式
结合借用构造函数传递参数和寄生模式实现继承
!传入的参数是构造函数!!!

  • 继承函数
    只需要调用一次父类构造函数
		function inheritPrototype(son,father){//参数是子类型父类型的构造函数
            var clone=Object.create(father.prototype);//创建超类原型的副本  
            clone.constructor=son;//为创建的副本添加constructor属性,矫正重写原型丢失的constructor
            son.prototype=clone;//将副本赋值给子类的原型
        }

在这里插入图片描述

  • 父类属性及方法
        function Animal(name){
            this.name=name;
            this.size=["big", "small"];
        }
        Animal.prototype.sayName=function(){
            console.log(this.name);
  • 借用构造函数继承属性
        function Cat(name,color){
            Animal.call(this,name);
            this.color=color;
        }
  • 继承方法
        inheritPrototype(Cat,Animal);
        //原型式继承 增加子类方法
        Cat.prototype.saySize=function(){
            console.log(this.size);
        }
        var HelloKitty=new Cat("HelloKitty","Pink");
        HelloKitty.sayName();//HelloKitty
        HelloKitty.saycolor();//Pink
        console.log(HelloKitty.size);//[ "big", "small" ]
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!