寄生组合继承
这是一种接近完美(接下来会展开)的继承方式,先来看其它一些继承方式有助于记忆;
原型链继承
function Parent(){
this.name = 'parent';
this.colors = ['red','orange','green'];
};
Parent.prototype.skill = 'run';
function Child(){
this.name = 'child';
};
Child.prototype = new Parent();
let child1 = new Child();
这就是原型链继承,
- 优点:继承了父类的模板和原型
- 缺点:
- 子类修改了父类的引用类型的变量,会影响到所有的子类;
- 创建子类时,无法向父类构造函数传参
关于缺点一 顺便来看下赋值和引用
let arr1 = ['red','orange','yellow'];
let arr2 = ['张三','李四','老王'];
let copyArr1 = arr1;
let copyArr2 = arr2;
copyArr1.push('green');
copyArr2 = ['张三','老王'];
console.log(arr1); // (4) ["red", "orange", "yellow", "green"]
console.log(arr2); // (3) ["张三", "李四", "老王"]
第一个是引用,第二个是赋值,给予了copyArr2 一个新的内存空间和原来的arr2无关了;
构造函数继承
function Parent(name){
this.name = name;
this.colors = ['red','orange','green'];
};
Parent.prototype.skill = 'run';
function Child(){
Parent.call(this,'我是parent');
this.name = 'child';
};
let child1 = new Child();
/*
Child {name: "我是parent", colors: Array(3), say: "Hi!"}
*/
console.log(child1);
console.log(child1.skill); // undefined
构造函数继承总结
- 优点
- 实例子类的时候可以扩展
- 可以传参
- 子类修改继承而来的引用类型的值不会影响父类
- 缺点
- 无法继承父类的原型
- 无法实现父类函数的复用,有多少个子类就需要调用多少次父类构造函数;
组合继承
function Parent(name){
this.name = name;
this.colors = ['red','orange','green'];
};
Parent.prototype.skill = 'run';
function Child(){
Parent.call(this,'我是parent');
this.say = 'Hi!';
};
Child.prototype = new Parent();
let child2 = new Child();
console.log(child2); // {name: "我是parent", colors: Array(3), say: "Hi!"}
console.log(child2.skill); // run
console.log(Child.prototype.constructor); // Parent
Child.prototype.constructor = Child;
组合继承总结
- 优点
- 可以继承父类的属性和方法,也可以继承父类原型链上的属性和方法;
- 弥补了原型链继承中引用属性共享的问题
- 可传参可复用
- 缺点
- 调用了两次父类构造函数
- 子类实例的同名属性会覆盖父类的同名属性,不容易维护
- 子类实例的构造函数意外指向了父类的构造函数,需要重新维护
寄生组合继承
// 原型链继承
function Parent(name){
this.name = name;
this.colors = ['red','orange','green'];
};
Parent.prototype.skill = 'run';
function Child(){
Parent.call(this,'我是parent');
this.say = 'Hi!';
};
// 和组合继承相比 只有这个地方有改动
// 等同于
Child.prototype = Object.create(Parent.prototype);
let child3 = new Child();
console.log(child3);
console.log(child3.skill);
console.log(Child.prototype.constructor);
Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
Child.prototype = Object.create(Parent.prototype);
等同于
Child.prototype.__proto__ = Parent.prototype;
寄生组合继承相较与组合继承来说改进了一点,少调用了一次父类的构造函数,其它没什么不同的;
寄生组合继承上边我们说是接近完美的继承,这种方式的缺点是什么呢?来看这样一个例子
function Car(color){
this.music = function(){
console.log('lalala');
}
this.skill = function(){
console.log('run');
}
this.addOil = function(){
console.log('100#');
}
}
Car这个父类构造函数,子类继承的话,是会继承到addOil这个方法,问题是,有的子类并不需要这个方法,比如新能源车,它是不需要加油的;
继承的最大问题在于:无法决定继承哪些属性,所有属性都得继承。
那怎样解决这类问题呢,用组合,就是先设计好零件,需要就加上
function music(){
console.log('lalala');
}
function skill(){
console.log('run');
}
function addOil(){
console.log('100#');
}
let car = compose(music,skill,addOil);
let newEnergyCar = compose(music,skill);
ES6 Class继承
和ES6 Class继承相关的就是constructor super
class Parent{
constructor(name){
this.name = name;
this.logFunc = function(){
console.log(this.name);
}
}
say = 'Hi!'
skill(params) {
console.log('running~~');
}
}
class Child extends Parent{
constructor(name){
super(name);
this.sex = 'boy';
}
eat = 'apple';
}
let child1 = new Child('child1');
// {say: "Hi!", name: "child1", eat: "apple", sex: "boy", logFunc: ƒ}
console.log(child1);
如果不写 constructor,子类继承的时候会默认加上 constructor,还可以这样写
function Parent(name){
this.name = name;
this.colors = ['red','orange','green'];
};
class Child extends Parent{
constructor(name){
super(name);
this.sex = 'boy';
}
eat = 'apple';
}
let child1 = new Child('child1');
// {name: "child1", colors: Array(3), eat: "apple", sex: "boy"}
console.log(child1);
super 是必写的,还是上边的代码,我们不写super,看会出现什么
function Parent(name){
this.name = name;
this.colors = ['red','orange','green'];
};
class Child extends Parent{
constructor(){
this.sex = 'boy';
}
eat = 'apple';
}
let child1 = new Child();
/*
Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
*/
关于报错我是这样理解的,suer的作用是创造父类的实例对象this,然后再用子类的构造函数去改变它。
因为ES6 class 继承采用的方法也是寄生组合继承,说明了这种方法的成功;
参考文章: