ES5四种继承方式+ES6类继承
一.借用构造函数
主要用到的原理:使用call函数改变this指向
function Parent() {
this.name = 'qx';
}
function Child(age) {
Parent.call(this);
this.age = age;
}
var c = new Child(19);
console.log(c.name); //qx
原理就是在Child的构造函数中,调用Parent构造函数,利用call改变this的指向,
此时的this是Child里面的,被调用后Child里面的this有了name属性
但是这种方法不能够继承到父函数原型链上的东西
//这里在父函数的原型链上添加一个方法,父函数自身没有这个函数的话,就会在原型链上寻找
Parent.prototype.hi = function () {
console.log('hi');
};
var p = new Parent(); //new一个Parent对象用来对比
p.hi(); //输出hi
var c = new Child(19);
c.hi(); //undifined,Child没有继承Parent对象的原型链,找不到这个方法
二.原型链继承
先再复习下这张图
function Parent() {
this.name = 'qx';
}
Parent.prototype.hi = function () {
console.log('hi');
};
function Child(age) {
this.age = age;
}
Child.prototype = new Parent();//让原型对象等于另一个类型的实例
var c = new Child(19);
console.log(c.name); //输出qx
//寻找name这个属性的过程:
//现在c内部属性中找一遍,没有找到,然后去c的原型也就是c.__proto__中找一遍,而看上面的图c.__proto__就是Child.prototype,而上面定义了Child.prototype = new Parent(),所以会直接去parent实例中寻找name,于是找到name='qx'
c.hi() //输出say
//调用这个方法和上面类似,一路寻找到parent实例发现也还是没有这个方法,于是继续去找这个实例的原型,也就是Parent.prototype,然后找到了这个方法
发现通过使原型=实例的方法,也就是原型链的方法,可以继承到实例所属类的原型链上的所有的属性和方法
但是这种方法存在一些缺陷:
-
引用值共享问题
引用值会被所有的实例,一个对象修改了原型属性,那么另一个的原型属性也会被修改 -
不能传参
在创建Child的实例时,不能向Parent传递参数,如果传递也不会有作用,也就是无法去修改父类的属性function Parent(name){ this.name = name||parent; this.value = 19; this.arry = [1,2,3]; } function Child(){ } Child.prototype = new Parent(); var child1 = new Child('child1'); var child2 = new Child('child2');
于是便有了组合继承
三.组合继承
就是将上面两种继承方式结合再一起
结合了两者的优点:
- Parent上的原型可以被继承
- 解决了引用值共享问题
- Child也可以向Parent传参数
function Parent(name) {
this.name = name || 'parent';
}
function Child(name, age) {
Parent.call(this, name); //继承实例属性,第一次调用Parent()
this.age = age;
}
Child.prototype = new Parent(); //继承原型,第二次调用Parent()
Child.prototype.constructor = Child;//修正构造函数为自己本身
var child1 = new Child('child1', 18);
var child2 = new Child('child2', 19);
但是这种方法还是不完美,因为这种方法调用了两次Parent(),会造成不必要的开销
为了解决这个问题,便又有了寄生组合式继承
四.寄生组合式继承
//借用大佬的一段代码,看了好久才看懂
function extend(subClass,superClass){
var prototype = Object(superClass.prototype);//创建对象
prototype.constructor = subClass;//增强对象
subClass.prototype = prototype;//指定对象
}
//可以去看看这位大佬的文章,收获挺大的
作者:路易斯
链接:https://juejin.cn/post/6844903475021627400
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
用自己的理解来解释下这三行代码:
首先是在函数中封装好这个继承方便之后直接用,subClass是子类,superClass是父类
创建对象:创建一个父类的原型对象,作为备用
增强对象:原本这个原型对象的constructor应该是父类的构建函数,但是被指向了子类的构造函数
指定对象:最好将这个原型指定为子类的原型
三行代码最后生成了一个拥有子类本身构造函数和父类原型的对象
最后想实现一个继承:
Parent.call(this, name); //继承第一步,继承实例属性,调用Parent()
extend(Child, Parent); //继承第二步,不会调用Parent()
具体代码如下:
function extend(subClass, superClass) {
var prototype = Object(superClass.prototype);//创建对象
prototype.constructor = subClass;//增强对象
subClass.prototype = prototype;//指定对象
}
function Parent(name) {
this.name = name || parent;
this.value = 19;
this.arry = [1, 2, 3];
}
function Child(name) {
Parent.call(this, name); //继承第一步,继承实例属性,调用Parent()
}
//Child.prototype = new Parent();
extend(Child, Parent); //继承第二步,不会调用Parent()
var child1 = new Child('child1');
var child2 = new Child('child2');
console.log(child1 instanceof Parent);//true
function Son() {
Child.call(this);
}
extend(Son, Child);
var son = new Son();
console.log(son instanceof Child);//true
console.log(son instanceof Parent);//true //这里就简单测试了下,父类的父类也可以算
最后其实还有几种,但是学了这个最好的,就不是很想去了解了~~
五.ES6类继承
ES6中就可以支持类来继承了,类似Java C++
class Point{
constructor(x,y){
this.x = x
this.y = y
}
toString(){
return this.x + '' + this.y
}
}
class ColorPoint extends Point{
constructor(x,y,color){
super(x,y) //调用父类的constructor(x,y)
this.color = color
}
toString(){
return this.color + ' ' + super.toString() //调用父类的toString()
}
}
let colorPoint = new ColorPoint('1','2','red')
console.log(colorPoint.toString()) //red 12
学了这么久,发现ES6居然是这么简便的方法 T T
小白记录学习笔记,欢迎大佬指正~~~~