浅谈JS中的原型继承、call继承以及寄生组合继承

JS中的继承指的是子类继承父类中的属性和方法,用的比较多的是原型继承,call继承,以及寄生组合继承,今天我们来简单讲解一下这三类继承。

  1. 原型继承
    	    function A() {
    		    this.x = 100;
    		}
    		A.prototype.getX = function getX() {
    		    console.log(this.x);
    		};
    
    		function B() {
    		    this.y = 200;
    		}	
    		B.prototype.sum=function(){}
    		B.prototype = new A;
    		B.prototype.getY = function getY() {
    		    console.log(this.y);
    		};
    		let b = new B;

    简单分析一下上面的代码块,第一行我们定义了一个父类A,A是一个函数对象,会给他分配一个内存地址,假设为FFFAAA001,A类有一个prototype属性指向它的原型,原型也是一个对象,假设内存地址为FFFAAA002,而原型对象也有一个constructor属性指向它的构造函数A;然后第四行代码在A的原型上添加了一个getX方法,如下图

    每一个内存地址里面装的都是对应的代码块,后来定义了一个子类B类,并在原型上定义了一个sum方法,如下图,

    关键点来了,注意这句,B.prototype = new A,这句什么意思呢?new A我们都知道new出来的是A类的一个实例,而实例上的_proto_属性是指向类的原型的;B.prototype刚刚我们也说了,就是我们子类B的原型对象,就是我们上图中的FFFBBB002这个内存地址;由上图我们可以知道,B.prototype = FFFBBB002,但现在我们手动的改变了B.prototype的内存地址,并在B.prototype里面定义了一个getY方法,看下图

    如上图所示,我们假设new A的实例的内存地址为FFFAAA003,那么原来的B.prototype指向已经从原来的FFFBBB002改为了FFFAAA003,我们可以在控制台输出看看效果

    图1是没有改变B.prototype指向的时候,图2是改变了指向的,很明显,图2的原型已经指向了实例A,并且,原型上也没有了原来定义的sum方法,并且后面添加的getY方法也是添加在了A实例上,至此,我们的原型继承已经完成了。
    最后我们来看一下let b = new B这行代码

    我们在控制台上输出一下b,

    很明显,b是B的一个实例,因为b是通过B类new出来的一个对象,因此,他可以使用B类上的属性和方法;b._proto_属性指向是A的实例,可以通过_proto_这条链子拿到A上面的属性和方法,就是我们上面画的箭头,然后A实例可以通过_proto_属性访问A.prototype,因此,b这个实例可以访问B类以及A类的属性和方法了。

  2. call继承
    直接上代码块

    function A() {
    	this.x = 100;
    }
    A.prototype.getX = function getX() {
    	console.log(this.x);
    };
    
    function B() {
    	//CALL继承
    	A.call(this); //=>this.x = 100;  b.x=100;
    	this.y = 200;
    }
    B.prototype.getY = function getY() {
    	console.log(this.y);
    };
    let b = new B;
    console.log(b);

    代码没什么不同,先创建A类,在A的原型上添加getX方法,再创建B类,在B类的原型上绑定getY方法,最后创建一个B类的实例,如下图

    我们都知道,继承就是子类继承父类的属性和方法,那么call继承是如何完成这一步的呢,那就是使用call来改变this指了,我们主要分析A.call(this)这句代码,在不加call时,也就是直接执行A(),那么A方法里面的this.x = 100就相当于window.x = 100,因为函数内部的this指向大多是由执行这个方法的对象决定的,执行A()相当于执行window.A(),那么this就是window,回到继承上,我们是想b这个实例能访问到A上面的方法,那么我们就得改变A里面的this指向,因此我们用call来改变this的指向,A.call(this),就是把当前调用方法B的对象(也就是b)传给A,A内部的this就变成了b,因此b.x = 100了。

  3. 寄生组合继承(CALL继承+变异版的原型继承共同完成)
     

    function A() {
    	this.x = 100;
    }
    A.prototype.getX = function getX() {
    	console.log(this.x);
    };
    
    function B() {
    	A.call(this);
    	this.y = 200;
    }
    //=>Object.create(OBJ) 创建一个空对象,让其__proto__指向OBJ(把OBJ作为空对象的原型)
    B.prototype = Object.create(A.prototype);
    B.prototype.constructor = B;
    B.prototype.getY = function getY() {
    	console.log(this.y);
    };
    let b = new B;
    console.log(b);

    从代码中可以看出,在执行创建空对象之前的代码和call继承的代码一模一样,在创建了两个构造类之后,将B的原型属性指向一个空对象的原型,Object.create(A.prototype)这句代码是指创建一个空对象,并将空对象的_proto_属性指向A.prototype,也就是把A.prototype作为空对象的原型,并手动的把B的原型对象上的constructor 属性指向B本身,因为空对象本身是不具有constructor 属性的,这样子会造成constructor 属性的缺失。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值