前言
提起继承我们最开始想到了应该是子类继承父类,接下来尽量用简洁的语言来表达清楚什么是继承
正文
原型继承
什么是原型继承
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
我们能够想到的是改变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 ~ ❤️❤️❤️❤️ 点关注 ?? 不迷路 点个赞哦?
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!