常用七种ES5继承方案和ES6的类继承共八种继承方法。
1、原型继承
把子类的原型指向要继承的父类
//父类
function ParentClass(name) {
this.name = name
}
ParentClass.prototype.eatName = 'xx';
ParentClass.prototype.eat = function (name) {
console.log('这是一个' + name);
};
//子类
function ChildClass(age) {
this.age = age
}
/* 将 ChildClass 的原型直接赋值为 ParentClass的构造函数,
所以此时ChildClass的原型对象中的 constructor其实是指向ParentClass的构造函数的,
所以我们这边要修改回来,不然引起原型链的絮乱 */
ChildClass.prototype = new ParentClass();
ChildClass.prototype.constuctor = ChildClass;
let obj = new ChildClass('12', 22);
obj.eat('西红柿');
console.log(obj.eatName); // xx
缺点:只能继承父类原型上的方法和属性,不能继承父类的实例属性和方法,多个实例对引用类型的操作会被篡改。
2、构造函数继承
使用parentClass.call(this)改变this的指向来调用父类的属性和方法
//父类
function ParentClass(name) {
this.name = name;
this.eat = function(){
console.log('这是一个' + name);
}
}
//子类
function ChildClass(name,age) {
ParentClass.call(this,name)
this.age = age
}
let obj = new ChildClass('西红柿', 22);
console.log(obj.name,obj.age);
obj.eat() // 这是一个西红柿
缺点:只能继承父类实例e的属性和方法,不能继承原型上的属性和方法
3、组合式继承
以上二种继承方式的组合
//父类
function ParentClass(name) {
this.name = name;
}
ParentClass.prototype.eatName = 'xx';
ParentClass.prototype.eat = function () {
console.log('这是一个' + this.name);
};
//子类
function ChildClass(name,age) {
ParentClass.call(this,name)
this.age = age
}
/*
将 ChildClass 的原型直接赋值为 ParentClass的构造函数,
所以此时ChildClass的原型对象中的 constructor其实是指向ParentClass的构造函数的,
所以我们这边要修改回来,不然引起原型链的絮乱
*/
ChildClass.prototype = new ParentClass();
ChildClass.prototype.constuctor = ChildClass;
let obj = new ChildClass('西红柿', 22);
console.log(obj.name,obj.age);
obj.eat() // 这是一个西红柿
4、原型式继承
利用Object.creat(parentClass)创建一个原型指向parentClass的对象
let parentClass = {
name: '西红柿'
};
let ChildClass = Object.create(parentClass);
ChildClass.age = 11;
console.log(ChildClass.name,ChildClass.age); //西红柿 11
缺点:无法传递参数,原型链继承多个实例时,实例引用类型指向相同,存在篡改的可能
5、寄生式继承
在原型上的基础上新增属性和方法增强函数
let parentClass = {
name: '西红柿'
};
function clone(original){
let obj = Object.create(original);
obj.eat = function(){
console.log('这是一个' + this.name); //西红柿
}
return obj;
}
let ChildClass = clone(parentClass);
console.log(ChildClass.name);
ChildClass.eat();
缺点:(同上原型式继承)无法传递参数,原型链继承多个实例时,实例引用类型指向相同,存在篡改的可能
6、寄生组合式继承
将组合式和寄生式结合起来
//父类
function ParentClass(name) {
this.name = name;
}
ParentClass.prototype.eat = function () {
console.log('这是一个' + this.name);
};
//子类
function ChildClass(name,age) {
ParentClass.call(this,name)
this.age = age
}
/*
这里改用 Object.create 就可以减少组合继承中多进行一次构造的过程
*/
ChildClass.prototype = Object.create(ParentClass.prototype);
ChildClass.prototype.constuctor = ChildClass;
let obj = new ChildClass('西红柿', 22);
obj.eat();
console.log(obj.age); // 22
目前最成熟的方法
7、混入方式继承多个对象
在寄生组合方法中的Object.creat()的下一行使用Object.assin()混合其它的类,
Object.assin()会把所有可枚举的属性从一个或多个原对象复制到目标对象。
//父类
function ParentClass(name) {
this.name = name;
}
ParentClass.prototype.eat = function () {
console.log('这是一个' + this.name);
};
function ParentClass1(count) {
this.count = count;
}
//子类
function ChildClass(name,age,count) {
ParentClass.call(this,name);
ParentClass1.call(this,count);
this.age = age
}
/*
这里改用 Object.create 就可以减少组合继承中多进行一次构造的过程
*/
ChildClass.prototype = Object.create(ParentClass.prototype);
//混合其它
Object.assign(ChildClass.prototype,ParentClass1.prototype)
ChildClass.prototype.constuctor = ChildClass;
console.log(ChildClass);
let obj = new ChildClass('西红柿', 22, 10);
obj.eat();
console.log(obj.age,obj.name,obj.count); // 22
8、ES6类继承
用extends继承父类,在子类的construtor调用super()。
//父类
class ParentClass {
constructor(name){
this.name = name
}
eat(){
console.log('这是一个' + this.name);
}
}
//子类
class ChildClass extends ParentClass{
constructor(name,age){
super(name)
this.age = age
}
}
let obj = new ChildClass('西红柿', 22);
obj.eat();
console.log(obj.name,obj.age); // 西红柿 22
ES5继承和ES6继承的区别:
ES5:先创建子类的实例对象,再把父类的属性和方法添加到this上。
ES6:先创建父类的实例对象this,再用子类的构造函数修改this。因为子类没有自己的this,所以必须调用父类的super()方法,否则新建实例会报错