好几天没写博客了,本篇文章,是近期对于继承方面的一些学习感悟,一直以来对继承的理解比较浅显,所以下定决心要研究一下,终于通过几天潜心学习,有所收获,以此博文来记录总结,方便日后复习。文中不足之处,请各位老师指点。
借鉴了以下各位前辈的博客
- https://blog.csdn.net/qq_32682137/article/details/82426401
- http://www.fly63.com/article/detial/2172
- https://www.jianshu.com/p/61b17c62c9fa
- https://segmentfault.com/a/1190000015565616
- https://blog.csdn.net/zhuanyemanong/article/details/80325387
学习继承前一定要弄明白prototype,constructor,_ _ proto _ _,还不太清除的小伙伴请看prototype,__ proto __,constructor到底是什么关系(图解)
结论:对比如下
特点 | 缺点 | |
---|---|---|
属 性 拷 贝 继 承 | 理解方便 | 1.相比于其他继承,性能较低 2.不可枚举属性for…in访问不到 3.对于引用类型*注释1的继承只是继承了指针,在父子对象中是共享的,一个改变全都改变 |
原 型 式 继 承 | 继承了父类原型上的属性和方法,当不需要构建构造函数,只想模拟一个对象时比较方便 | 1.不能继承父构造函数的实例对象的成员 2.一个子类原型更改子类原型从父类继承来的引用类型属性就会直接影响其他子类 |
原 型 链 继 承 | 子类不仅仅可以访问父类原型上的属性和方法,同样也可以访问从父类构造函数中复制的属性和方法 | 1.一个子类原型更改子类原型从父类继承来的引用类型属性就会直接影响其他子类; 2.由于子类实现的继承是靠其原型对父类的实例化实现的,因此在创建父类的时候,是无法向父类传递参数的,因而在实例化父类时也无法对父类构造函数的属性进行初始化。 |
构 造 函 数 继 承 | 避免了引用类型的属性被所有实例共享,而且可以在子类中向父类传参 | 1.这种类型的继承没有涉及原型prototype,所以父类的原型方法自然不会被子类继承 2.因为方法和属性只能写在构造函数中,因此不能实现函数复用,这样就违背了代码复用的原则 |
组 合 继 承 | 继承原型链和构造函数的优点,能通过instanceOf和isPrototypeOf的检测 | 两次调用父构造器函数,浪费内存。 |
寄 生 组 合 继 承 | 最优继承方法 | 暂无 |
ES6 继 承 | 1.引入关键词class 2.继承机制与ES5完全不同 (只是看似不同,其实原理还是原型继承) | 暂无 |
注释1:引用类型指的是,Function,Array,Object,这几种类型在赋值的时候只会把存在栈区的指针拷贝一份赋值过去,即
let a = [1, 2];
let b = a;
b.push(3);
console.log(a); // [1, 2, 3]
1.拷贝继承
// 创建父对象
var father = {
name: 'father',
age: 50,
friends: ['李阿姨', '王阿姨', '赵姐'],
showName: function () {
console.log(this.name);
}
};
// 创建需要继承的子对象
var children = {};
// 开始拷贝属性(使用for...in...循环)
for (var i in father) {
children[i] = father[i];
}
console.log('children', children);
console.log('father', father);
2.原型式继承
function object(o){
function F(){}
F.prototype = o;
return new F();
}
3.原型链继承
function SuperType(){
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.Fun = function(){
};
function SubType(){
}
//继承了SuperType
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
let instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors); //"red,blue,green,black"
let instance2 = new SubType();
console.log(instance2.colors); //"red,blue,green,black"
4.构造函数继承
function SuperType(){
this.colors = ["red", "blue", "green"];
}
function SubType(){
//继承了SuperType
SuperType.call(this);
}
let instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
//解决了修改一个实例的值,全都改变的问题
console.log(instance2.colors); //"red,blue,green"
*****************************************************
function SuperType(name){
this.name = name;
}
function SubType(){
//继承了SuperType,同时还传递了参数
SuperType.call(this, "Nicholas");
//实例属性
this.age = 29;
}
let instance = new SubType();
console.log(instance.name); //"Nicholas";
console.log(instance.age); //29
5.组合继承
function SuperType(name){ //可以传参
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
console.log(this.name);
};
function SubType(name, age){
SuperType.call(this, name);//借用构造函数继承属性,二次调用
this.age = age;
}
SubType.prototype = new SuperType();//借用原型链继承方法,一次调用
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
console.log(this.age);
};
let instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
console.log(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29
let instance2 = new SubType("Greg", 27);
console.log(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27
6.寄生组合继承
//不用实例化父类,直接实例化一个临时副本
function inheritPrototype(subType, superType){
let prototype = Object(superType.prototype); //创建对象
prototype.constructor = subType; //增强对象
subType.prototype = prototype; //指定对象
}
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
console.log(this.name);
};
function SubType(name, age){
SuperType.call(this, name);
this.age = age;
}
inheritPrototype(SubType, SuperType);//实现继承
SubType.prototype.sayAge = function(){
console.log(this.age);
};
上面代码不太直观,配合图片可以更好理解。
上面的图片中是还没有建立继承关系时,构造函数
和实例
之间的关系,其中构造函数实例化的对象会从其构造函数的prototype
对象上继承属性和方法,上图中SunType和SuperType暂时还没有继承关系,当我们调用了inheritPrototype()
之后,关系图变成如下:
超类(SuperType)的原型复制了一份,然后用这个副本替换了原来子类的原型,以达到继承的目的。
7.ES6继承
需要注意的是,在子类中使用 this
需要先调用super(...)
- 当一个普通的构造函数运行时,它会创建一个空对象作为 this,然后继续运行。
- 但是当派生的构造函数运行时,它指望父构造函数来完成这项工作。
class Animal {
constructor(name) {
this.speed = 0;
this.name = name;
}
// ...
}
class Rabbit extends Animal {
constructor(name, earLength) {
super(name);
this.earLength = earLength;
}
// ...
}
let rabbit = new Rabbit("White Rabbit", 10);
alert(rabbit.name); // White Rabbit
alert(rabbit.earLength); // 10
关于super的底层实现,等我研究明白了再来补充。
2020/06/02,重新回来补充知识点了,关于 super 的相关知识点,请查看我的这篇微博