JavaScript的八种继承方法
常用七种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()方法,否则新建实例会报错