JavaScript 里边的6种继承——0627笔记整理

继承: 子类继承父类的属性和方法(要继承,必须要有父类)。

//父类 
    function Person(a) {    //给父类构造函数添加参数
        this.name = a;
        this.age = null;
        this.sex = "男";
        this.sleep = function () {
            return "睡觉";
        }
    }
    Person.prototype.height = 100;    //给父类的构造函数添加原型属性

javascript中的每个对象都有prototype属性,Javascript中对象的prototype属性的解释是:返回对象类型原型的引用。

一、原型链继承(常用)

什么是原型链继承?

由外到内,通过__proto__属性连接,形成链式,就叫原型链。
原型链继承是通过prototype属性实现继承的。

    var arr = new Array();
    //实例化数组对象arr  继承了Array类对象的属性和方法
    console.log(arr);
    console.log(Math);

通过上面两个内置对象可以了解到,js底层就是通过原型链继承而来的。

原型链继承的特点:

1.子类的实例继承:子类的构造属性和方法、父类的构造属性和方法、父类的原型属性和方法
2.子类对象的实例既是子类的实例,也是父类的实例。
3.父类的构造函数只执行一次

缺点:

1.不能继承子类的原型属性和方法(在继承之前给子类添加原型属性和方法无效)
2.只能进行单继承,不能多继承(继承单一)
3.继承之后,原型对象上的属性全是共享的(所有新实例都会共享原型对象上的属性)
4.新实例无法向父类构造函数传参。

举例:

    //父类对象的构造函数
    function Person(a) {
        this.name = a;  //this指向当前类的实例化对象
        this.age = null;
        this.sex = "男";
        this.sleep = function () {
            return "睡觉";
        }
    }
    Person.prototype.height = 100;
    //子类对象的构造函数
    function Child() {
        this.job = null;
    }
    //原型链继承不能直接继承子类的原型属性和方法   简单说相当于被覆盖了(Child.prototype=new Person();)
    //即:在原型链继承之前 给子类添加原型属性和方法无效
    Child.prototype.eat = function () {
        return "吃饭";
    };
    //原型链继承(在实例化子类之前)  核心是让子类的原型等于父类的实例
    Child.prototype = new Person("张三");
    //在继承之后给子类添加原型属性和方法  是直接添加给子类的原型对象(不会添加给父类)
    Child.prototype.weight = 50;
    //实例化子类对象
    var child = new Child();
    var child1 = new Child();
    console.log(child, child1);  //原型对象上的属性共享

    console.log(Child.prototype);   //没有原型链继承,子类的原型对象指向自身
    console.log(Child.prototype);  //原型链继承之后,子类的原型对象指向父类

    //继承之后对象的问题
    //obj instanceof class  用来测试一个对象obj是否为另一个类class的实例
    console.log(child instanceof Person);  //true
    console.log(child instanceof Child);   //true
	
	//子类实例child的原型链
	console.log(child.__proto__);  //Person
    console.log(child.__proto__.__proto__);   //Person的原型对象
    console.log(child.__proto__.__proto__.__proto__);   //Object的原型对象
    console.log(child.__proto__.__proto__.__proto__.__proto__);  //Object  的 __proto__ null

1.类对象的构造函数里边的this 指向当前类的实例化对象。
2.原型链继承的核心,就是让子类的原型(prototype)等于父类的实例。
3.如果不设置原型对象,直接实例化,默认继承Object,原型对象的构造函数constructor指向自身。
4.构造函数在类被实例化的时候直接执行。
5.在继承之后给子类添加原型属性和方法,是直接添加给子类的原型对象(不会添加给父类)

二、构造继承(常用)

什么是构造继承?

构造继承是通过.call() / .apply() 来实现继承的。(用.call().apply()将父类构造函数引入子类函数,即:在子类函数中做了父类函数的自执行(复制))。

优点:

1.可以实现多继承。
2.可以向父类传递参数。
3.解决了原型链继承中原型属性共享的缺点

缺点:

1.只能继承父类构造函数的属性和方法,无法继承父类的原型属性和方法
2.子类对象是子类的实例,不是父类的实例
3.无法实现构造函数的复用(每次用的时候都要重新调用)
4.每个新实例都有父类构造函数的副本,臃肿。

举例:

    //父类一
    function Animal(){
        this.name=arguments[0];
        this.sleep=function(){
            return "睡觉";
        }
    }
    Animal.prototype.color=null;
    //父类二
    function Type(){
        this.type=arguments[0];
    }
    //子类
    function Dog(n,s,t){
        this.sex=s;
        this.eat=function(){
            return "吃饭";
        };
        //这里写构造继承
        Animal.call(this,n);
        Type.apply(this,[t])
    }
    //实例化子类对象  传参
    var dog=new Dog("小黑","母的","犬科");
    console.log(dog);

    //子类对象是子类的实例 不是父类的实例
    console.log(dog instanceof Dog);  //true
    console.log(dog instanceof Animal);  //false
    console.log(dog instanceof Type);  //false

    //子类的原型对象
    console.log(Dog.prototype);  //构造函数指向自身

    //子类实例的原型链
    console.log(dog.__proto__);  //Object   Dog的原型对象
    console.log(dog.__proto__.__proto__);  //Object   Object的原型对象

三、实例继承

什么是实例继承?
在子类内部直接构造父类的实例,直接new父类。

优点:

不限制调用方式(子类实例有两种写法)
可以向父类传递参数

缺点:

不能实现多继承
不能拿到子类构造属性和方法
子类对象的实例不是子类的实例,而是父类的实例

举例:

    //父类
    function Person(n,s){
        this.name=n;
        this.sex=s;
        this.sleep=function(){
            return "睡觉";
        }
    }
    //子类
    function Child(n,s){
        this.eat=function(){
            return "吃饭";
        };
        return new Person(n,s);
    }
    //实例化子类对象  两种写法
    var child=Child("张三","男");
    console.log(child);  //不能拿到子类的构造属性和方法  eat
    var child1=new Child("张三","男");
    console.log(child1);  //不能拿到子类的构造属性和方法  eat

    //检测子类对象的实例  是父类的实例  不是子类的实例
    console.log(child instanceof Child);  //false
    console.log(child instanceof Person);  //true

    //子类的原型对象
    console.log(Child.prototype);  //自身

    //子类对象实例的原型链
    console.log(child.__proto__);  //person的原型对象
    console.log(child.__proto__.__proto__);  //Object的原型对象

四、拷贝继承

什么是拷贝继承?
在子类里面,将父类里面的方法和属性拷贝给子类的原型对象 prototype

优点:

支持多继承
可以继承父类构造函数的属性和方法、也可以继承父类原型对象的属性和方法
子类对象的实例是子类的实例,不是父类的实例
可以向父类传递参数

缺点:

在继承的时候不断new,占内存

举例:

    //父类一
    function Animal(){
        this.name=arguments[0];
        this.sleep=function(){
            return "睡觉";
        }
    }
    Animal.prototype.color="black";
    //父类二
    function Type(){
        this.type=arguments[0];
    }
    //子类
    function Cat(n,s,t){
        this.sex=s;
        this.eat=function(){
            return "吃饭";
        };
        //拷贝父类里边的属性和方法  拷贝给prototype
        //先实例化父类对象  传参
        var animal=new Animal(n);
        for(var key in animal){
            Cat.prototype[key]=animal[key];  //注意这里的Cat不能写成this,因为这里的this指向是类的实例化对象,没有prototype属性
        }
        var type=new Type(t);
        for(var key in type){
            Cat.prototype[key]=type[key];
        }
    }

    //实例化子类对象
    var cat=new Cat("小花","母","猫科");
    console.log(cat);

    //子类的原型对象
    console.log(Cat.prototype);  //自身

    //检测子类对象的实例   是子类的实例  不是父类的实例
    console.log(cat instanceof Cat);  //true
    console.log(cat instanceof Animal);  //false
    console.log(cat instanceof Type);  //false

    //子类对象实例的原型链
    console.log(cat.__proto__);  //Cat的原型对象
    console.log(cat.__proto__.__proto__);   //Object的原型对象

五、组合继承

什么是组合继承?
原型链继承+构造继承
互相弥补对方的缺点,结合了两种方式的优点

优点:

子类对象的实例既是子类的实例,也是父类的实例
可以实现多继承(但是只能继承其中一个父类的原型属性)
可以向父类传递参数
每个新实例引入的构造函数属性是私有的(构造函数的属性和方法不是共享的)。

缺点:

调了两次父类的构造函数(耗内存),会有重复属性

举例:

    //父类
    function Person(n){
        this.name=n;
        this.eat=function(){
            return this.name+"吃饭";
        }
    }
    Person.prototype={
        constructor:Person,
        color:null
    };
    //子类
    function Child(a,s,n){
        this.age=a;
        this.sex=s;
        this.sleep=function(){
            return "睡觉";
        };
        //构造继承 只能继承父类构造函数的属性和方法  不能继承父类原型对象的属性和方法
        Person.call(this,n)
    }

    //原型链继承
    //父类的构造函数属性和方法、父类原型属性和方法全部传给子类原型对象
    Child.prototype=new Person();  //注意 这里没传值  
    //实例化子类对象
    var child=new Child(12,"女","小花");
    console.log(child);
    var child1=new Child(13,"男","小明");
    console.log(child1);

    //检测子类对象实例  是子类的实例  也是父类的实例
    console.log(child instanceof Child);  //true
    console.log(child instanceof Person);  //true

    //子类对象实例的原型链
    console.log(child.__proto__);  //Person
    console.log(child.__proto__.__proto__);  //Person的原型对象
    console.log(child.__proto__.__proto__.__proto__);  //Object的原型对象

六、寄生组合继承

寄生组合继承处理了组合继承的缺点,避免调用两次父类的构造函数,避免重复属性和方法。
原理:
将父类的原型对象给一个空对象的prototype,再把空对象和子类的原型对象关联。

举例:

    //父类
    function Person(){
        this.name=arguments[0];
        this.sleep=function(){
            return "睡觉";
        }
    }
    Person.prototype={
        constructor:Person,
        color:null
    };
    //子类
    function Child(n,a){
        this.age=a;
        this.eat=function(){
            return "吃饭";
        };
        //构造继承
        Person.call(this,n)
    }

    (function(){
        var fn=function(){};
        //寄生
        fn.prototype=Person.prototype;
        //原型链继承
        Child.prototype=new fn();
    })();

    //实例化子类对象
    var child=new Child("张三",12);
    console.log(child);

    //检测子类的实例化对象
    console.log(child instanceof Child);  //true
    console.log(child instanceof Person);  //true

    //子类实例化对象的原型链
    console.log(child.__proto__);  //Person
    console.log(child.__proto__.__proto__);  //Person的原型对象
    console.log(child.__proto__.__proto__.__proto__);   //Object
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值