前言
提起继承我们最开始想到了应该是子类继承父类,接下来尽量用简洁的语言来表达清楚什么是继承
正文
原型继承
让子类的原型
Child.prototype等于父类的实例new Parent
什么是原型继承
function Parent(){
this.dd = 'qq';
}
Parent.prototype.getDd = function(){
return this.dd
}
function Child(){
this.name = 'qq';
}
Child.prototype.getName = function(){
return this.name
}
let child = new Child;
此时思考一下child能获取或调用哪些方法或属性?答案肯定是可以获取name属性,以及可调用getName方法,所以我们能够发现,child实例可调用自己Child类私有的以及Child类原型prototype上公有的方法。
那么此时我们想让child继承父类Parent的属性,我们只需要将Child.prototype=new Parent,因为new Parent是Parent的实例,可以调用自己类私有以及类原型上的公有方法。这样我们便达到了原型继承的目的。
function Parent(){
this.dd = 'qq';
}
Parent.prototype.getDd = function(){
return this.dd
}
function Child(){
this.name = 'qq';
}
Child.prototype = new Parent;
Child.prototype.getName = function(){
return this.name
}
let child = new Child;
这样我们创建的实例child便可以调用getDd以及child.dd,但是我们也要注意代码添加的位置,如果我们在倒数第二行添加,则会导致getName属性消失。
接下来我们在控制台输出child,打印结果如下图,可能你会对打印结果存在疑惑,为什么是这样一层一层包下去的呢?接下来我们解释一下结果的产生:
原型链如下图所示,我们能够看到蓝色的①②③,这就是执行时原型链的查找顺序:
-
首先在①处,存在属性
name: 'qq' -
接下来按照②
child.__proto__方向进行查找,找到属性dd: 'qq', getName: function ... -
继续按照③
new Parent.__proto__方向查找,找到Parent.prototype里面的属性constructor, getDd -
...
这就是child的结果的产生。
总结
-
父类中私有和公有的属性方法,最后都变为子类实例公有的
-
和其他语言不同的是,原型继承并不会把父类的属性方法【拷贝】给子类,而是让子类实例基于
__proto__原型链找到定义的属性和方法,"指向/查找"方式的 -
在上面的实例
child中,通过child.__proto__.xxx = xxx来修改子类原型(原有父类的一个实例)中的内容,内容被修改后,对子类的其他实例有影响,但是对父类的其他实例不会有影响 -
通过
child.__proto__.__proto__.xxx = xxx直接修改的使父类原型,这样不仅会影响父类的其他的实例,也会影响其他子类的实例
CALL继承
什么是CALL继承
对于call我们能够想到的是改变this指向,并且最常见的使用是A.call(B),将A执行,并强制将this改变为B,所以接下来也将使用这种方式来理解CALL继承。
首先我们要先明白一件事,在类中this指向的是实例,也就是说在Child中this为实例child,所以执行的this.name是在实例child上添加私有属性。所以我们看下面的代码:
function Parent(){
this.dd = 'qq';
}
Parent.prototype.getDd = function(){
return this.dd
}
function Child(){
Parent.call(this);
this.name = 'qq';
}
Child.prototype.getName = function(){
return this.name
}
let child = new Child;
我们添加了 Parent.call(this);这样一行代码,在子类中调用父类执行并强制将Parent中的this修改为子类的实例对象child,此时Parent执行时this.dd = 'qq'相当于给实例child上添加属性dd.相当于让子类的实例继承了父类的私有的属性,并且也变为了为子类私有的属性"拷贝式"
总结
CALL继承只能继承父类中私有的,不能继承父类中公共的
寄生组合继承(CALL继承 + 另类原型继承)
根据上面的两种继承,第一种实现了将父类的私有以及公有属性都继承了,对于子类实例讲,父类的私有属性却变成了公有有。第二种便只能实现私有属性的继承,将父类的私有属性变为子类实例的私有属性。而我们最想要的是:将父类的私有属性,变为子类的私有属性(这个使用CALL继承已经实现),将父类的公有属性,变为子类实例的公有属性。
实现思路:在CALL继承的基础上实现将父类的共有属性,变为子类实例的公有属性
上图为CALL继承的原型链,我们能够发现现在只需要对Child.prototype.__proto__与Parent.prototype之间建立连接就可以实现,所以我们的完整代码为:
function Parent(){
this.dd = 'qq';
}
Parent.prototype.getDd = function(){
return this.dd
}
function Child(){
Parent.call(this);
this.name = 'qq';
}
Child.prototype.__proto__ = Parent.prototype
Child.prototype.getName = function(){
return this.name
}
let child = new Child;
根据上图说明我们的效果已经实现了,但是由于在低版本浏览器中对__proto__的兼容性不好,所以我们在进行优化一下:
function Parent(){
this.dd = 'qq';
}
Parent.prototype.getDd = function(){
return this.dd
}
function Child(){
Parent.call(this);
this.name = 'qq';
}
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child;
Child.prototype.getName = function(){
return this.name
}
let child = new Child;
说明:我们将Child.prototype的指向改变了,可以将Object.create(Parent.prototype)理解为创建了一个空对象,然后将这个空对象.__proto__指向了Parent.prototype,但是这时候子类的原型上丢失了constructor属性,所以我们手动添加此属性,保存完整性。所以实现了将父类的共有属性,变为子类实例的公有属性。
ES6中的类和继承
class Parent {
constructor(){
this.x = 100;
}
getX(){
return this.x
}
}
class Child {
constructor(){
this.y = 100;
}
getY(){
return this.y
}
}
let c1 = new Child;
说明:
-
在
ES6中,通过class创建类,其中constructor相当于类原型上的constructor,getX相当于在类的原型上添加属性。 -
想要实现继承需要在上面的例子中添加以下代码
class Child extends Parent{ constructor(){ super(); this.y = 100; } getY(){ return this.y } }①.
extends表示继承,后面的表示Parent继承谁的属性。②. 当使用继承时一定要在
constructor中加入super,相当于我们之前的CALL继承,super执行就等同于把Parent中的constructor执行
最后
biu biu biu ~ ❤️❤️❤️❤️ 点关注 🙈🙈 不迷路 点个赞哦😘