web(原型链与继承)

拜拜、爱过 提交于 2020-03-11 10:08:31

一、 原型链

  _proto_ : 原型链,对象的属性,指向生成该对象的构造函数的原型对象
 prototype: 原型对象,函数(构造函数)特有的属性,存储共享的属性和方法

 // 所有的javascript对象都会继承一个prototype原型对象  继承原型对象上的属性和方法
 // Date  对象  从Date.prototype 原型对象上继承属性和方法
 // Array 对象  从Array.prototype  原型对象上继承属性和方法

以js数组对象Array为例:

    var arr = new Array();
    console.log(typeof Array);
    //输出function,即Array本质是一个函数,js中函数也是对象
    
    console.log(arr.__proto__);
     //输出的是Array(arr的上一层,即创建arr的构造函数)的原型对象
     
    console.log(arr.__proto__.__proto__);
    //输出的是Object(即Array的上一层)的原型对象
    
    console.log(arr.__proto__.__proto__.__proto__);
    //输出为null  Object为最高层  没有继承任何对象

原型链与圆形函数的关系,通过下面的例子作以说明

    //定义一个Person 父类
    function Person() {
        this.color = "";
        this.sleep = function () {
        }
    }
    
    //给Person类添加原型对象
    Person.prototype = {
        constructor: Person,
        name: "",
        sex: "",
        eat: function () {
           return "eat";
        }
    }
    
     //定义一个子类Child
     function Child() {
        this.job = "";
    }
  
    // 子类  原型继承 父类
    Child.prototype = new Person();  //先继承,操作子类构造函数的prototype属性
    var c = new Child();             //再实例化子类对象
    
    console.log(c.__proto__);          //输出父类
    console.log(c.__proto__.__proto__);//输出父类的原型对象
    console.log(c.__proto__.__proto__.__proto__);          //输出Object 原型对象
    console.log(c.__proto__.__proto__.__proto__.__proto__);//输出null

 /*下面这种继承没有意义,之前自己在练习过程中上下两种输出一致,对此理解有误,之后发现了问题*/
 /*×××该方法请勿采用×××*/
    var c1=new Child();          //先实例化对象
    c1.__proto__=new Person();   //再继承,操作子类实例化对象的"__proto__"对象
    
    console.log(c1.__proto__);          //输出父类
    console.log(c1.__proto__.__proto__);//输出父类的原型对象
    console.log(c1.__proto__.__proto__.__proto__);         //输出Object 原型对象
    console.log(c1.__proto__.__proto__.__proto__.__proto__);//输出null

    /*两个实例化对象以上的输出一致*/
    
    console.log(Child.prototype == c.__proto__);  //输出true
    /* 第一种实例化:
    *  Child的prototype  通过 Child.prototype = new Person()   设置为了 Person
    *  因此Child实例化后的对象的__proto__也为person
    */
    
     console.log(Child.prototype == c1.__proto__);  //输出false
     /*第二种实例化:
     *先实例化后再继承,只对实例化后的c1对象进行了 c1.__proto__=new Person()的操作  
     *因此,Child的prototype未曾设置,c1的__proto__为person;
     */
     

总结:
prototype为函数特有的,__proto__是对象的属性
构造方法. prototype== 实例对象.__proto__

二、继承

  1. 原型链继承:将父类的实例化对象赋给子类的原型对象
    //原型链继承

    //定义一个父类
    function Person(name,age){
        this.name=name;
        this.age=age;
        this.eat=function(){
            return this.name+"吃饭";
        }
    }
    //为父类增添原型属性
    Person.prototype.color="黄种人";
    //为父类增添原型属性方法
    Person.prototype.sleep=function(){
        return this.name+"睡觉";
    };
    //定义一个子类
    function Child(sex){
      this.sex=sex;
        this.drink=function(){
            return this.name+"喝水";
        }
    }
    //将父类Person的实例化对象 赋给子类Child的原型对象prototype
    Child.prototype=new Person("张三","20");
    //实例化子类对象
    var child = new Child("男");
    //输出自身、父类、父类原型所有的属性及方法
    console.log(child);

    //判断child对象是否为构造类Child Person的实例化对象
    console.log(child instanceof Child);  //true
    console.log(child instanceof Person);   //true

优点:
1.实例即是子类的实例,也是父类的实例
2.子类本身,父类本身,父类原型,父类原型新增的所有属性方法,子类都可以访问到
缺点:
1.子类实例无法直接向父类构造方法传参;
2.继承之后原型对象上的属性全部是共享的
3.不能多继承 ,即一个子类不能继承多个父类

2.构造继承:通过call apply将父类属性及方法完全复制给子类

//定义父类Person
    function Person(name,sex){
        this.name=name;
        this.sex=sex;
        this.eat=function(){
            return this.name+"吃饭";
        }
    }
    //定义父类 Country
    function Country(con){
        this.con=con;
    }
    //添加父类原型属性
    Country.prototype.fn="父类原型";  //子类不能继承
    //定义子类
    function Child(age,n,s,c){
        this.age=age;
        Person.apply(this,[n,s]);    //继承Person父类
        Country.call(this,c);        //继承Country父类
    }

    var child=new Child(20,"张三","男","China");
    console.log(child);

    console.log(child instanceof  Child);  //true
    console.log(child instanceof Person);  //false
    console.log(child instanceof  Country); //false

优点:
1.可以多继承
2.子类实例可向父类构造函数传参
缺点:
1.构造继承只能call apply
2.子类只能继承 父类的构造属性和方法,不能继承父类原型的属性和方法
3.实例对象只是子类的实例,不是父类的实例

补充:call() 与 apply()方法

每个函数都包含两个非继承而来的方法:call()apply()
两方法作用均为:改变函数体内部 this 的指向

call(thisObj,参数1,参数2...);
apply(thisObj,[参数1,参数2...]);

3.实例继承:在子类里直接实例化父类(子类的实例返回 给父类添加的属性和方法)

 function Person(name,sex){
        this.name=name;
        this.sex=sex;
        this.eat=function(){
            return this.name+"吃饭";
        }
    }
    Person.prototype.fn="父类原型";  //子类不能继承

    function Child(n,s){
       var child=new Person(n,s);  //子类内部实例化父类
        return child;
    }

    var child=new Child("张三","男");
    console.log(child);   ////直接输出实例化后的Person对象

    console.log(child instanceof  Child);  //false
    console.log(child instanceof  Person);  //true

优点:
1.可以传递给父类参数
2.不限制调用的方式
缺点:
1.不能多继承
2.实例是父类的实例,不是子类的实例

4.拷贝继承:将父类对象中的每个属性和方法遍历复制到子类的原型中

 //父类Person
    function Person(name){
        this.name=name;
        this.eat=function(){
            return this.name+"吃饭";
        }
    }

    //父类Country
    function Country(con){
        this.con=con;
    }
    function Child(age,n,c){
        this.age=age;
        var per=new Person(n);
        var per1=new Country(c);
        for(var p in per){
            Child.prototype[p]=per[p];  //将父类实例的属性和方法复制给资料类的原型
        }
        for(var key in per1){
            Child.prototype[key]=per1[key];
        }
    }

    var child=new Child(20,"张三","china");
    console.log(child);
    console.log(child instanceof  Child);  //true
    console.log(child instanceof  Person);  //false
    console.log(child instanceof  Country);  //false

实例是子类的实例,不是父类的实例
优点:
1.可以多继承
2.子类可以向父类传递参数
缺点:
1.效率较低,内存占用高

5.组合继承:原型链继承+构造继承

//父类Person
    function Person(name) {
        this.name = name;
        this.sleep = function () {
            return this.name + "睡觉";
        }
    }

    //父类 Country
    function Country(con){
        this.con=con;
    }
    function Child(sex,n,c){
        this.sex=sex;
        Person.call(this,n);  //可多继承
        Country.call(this,c);
    }

    Child.prototype=new Person();

    //可直接向父类传参,弥补了原型链继承的缺点
    var child = new Child("男","张三","China");
    console.log(child);
    console.log(child instanceof  Child);  //true
    console.log(child instanceof  Person);  //true
    console.log(child instanceof  Country);  
    // 子类没有通过原型链继承Country  false

优点:
1.实例可以直接向父类传参
2.没有原型对象属性的共享
3.可多继承
4.实例是子类的,也是父类的
缺点:
调用了两次父类的构造函数

6.寄生组合继承:将父类的原型给予一个空对象的原型

//父类
    function Person(name) {
        this.name = name;
        this.sleep = function () {
            return this.name + "睡觉";
        }
    }
  //子类
    function Child(n, age) {
        this.age = age;
        this.eat = function () {
            return this.name + "吃饭"
        }
        Person.call(this,n);
    }

    //寄生
    (function(){
        var fn=function(){
        };
        //将父类的原型对象给予空对象
        //避免两次调用父类的构造函数
        fn.prototype=Person.prototype;
        Child.prototype=new fn();
    })();

    var child=new Child("张三",20);
    console.log(child);

    console.log(child instanceof Child); //true
    console.log(child instanceof Person);//true

优点:
处理了组合继承的缺点 ,避免两次调用父类的构造函数
实例是子类的,也是父类的

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!