js
中继承的实现主要是通过原型链实现的,下面就一起来一下实现继承的方式吧(继承也是面试中常被问到的一个点哦)
原型链继承
-
将父类的实例作为子类的原型实现继承
function SuperType(){ this.property = true; } SuperType.prototype.getSuperValue = function(){ console.log(this.property); } function SubType(){ this.subProperty = false; } SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function(){ console.log(this.subProperty); } let sub = new SubType(); console.log(sub);
-
特点:1)很纯粹的继承,实例是子类的实例,也是父类的实例
-
缺点:
- 1)父类的实例属性变成了子类的原型属性,由于原型属性是共享的,一个实例修改了原型属性,会造成其它实例的原型也被修改,如下:
- 2)子类型在实例化时不能给父类型的构造函数传参
function SuperType(){ this.colors = ['red', 'blue']; } function SubType(){ } SubType.prototype = new SuperType(); let instance1 = new SubType(); instance1.colors.push('black'); console.log(instance1.colors); // ["red", "blue", "black"] let instance2 = new SubType(); console.log(instance2.colors); // ["red", "blue", "black"]
盗用构造函数
-
通过
call
在子类函数中实现父类函数的自执行function SuperType(name){ this.colors = ['red', 'blue']; this.name = name; } function SubType(name){ SuperType.call(this, name); } let instance1 = new SubType('Zhang'); instance1.colors.push('black'); console.log(instance1); // ["red", "blue", "black"] let instance2 = new SubType('Li'); console.log(instance2); // ["red", "blue"]
-
特点:
- 1)继承了父类构造函数的属性,无法继承父类原型上的属性
- 2)可以给父类构造函数进行传参
- 3)可以继承多个构造函数的属性(
call
多个)
-
缺点:
- 1)必须在构造函数中定义方法,因此函数不能复用
- 2)每个新实例都有父类构造函数的副本,臃肿
- 3)子类不行访问父类原型上定义的方法
组合继承(结合原型链和盗用构造函数)
-
使用原型链继承原型上的属性和方法
-
通过盗用构造函数继承实例属性
function SuperType(name){ this.colors = ['red', 'blue']; this.name = name; } 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.sayAge = function(){ console.log(this.age); } let instance1 = new SubType('Zhang',2); instance1.colors.push('black'); console.log(instance1); // ["red", "blue", "black"] let instance2 = new SubType('Li', 3); console.log(instance2); // ["red", "blue"]
-
特点:组合继承弥补了原型链和盗用构造函数的不足,是
js
中使用最多的继承模式 -
缺点:调用了两次父类构造函数(耗内存)
原型式继承
-
借助原型,基于已有的对象创建并返回新的对象。本质上,
object
函数是对穿传入的对象执行了一次浅复制; -
ECMAScript 5
通过增加Object.create
方法将原型式继承的概念规范化了function object (o){ function Fn(){} Fn.prototype = o; return new Fn(); } let person = { name: 'Nicholes', friends: ['Court', 'Van'] } let anotherPerson = object(person); anotherPerson.name = 'Greg'; anotherPerson.friends.push('Rob'); let yetAnotherPerson = object(person); yetAnotherPerson.name = 'Linda'; yetAnotherPerson.friends.push('Barbie'); console.log(anotherPerson, yetAnotherPerson);
-
特点: 类似于将一个对象浅复制,适用于不需要单独创建构造函数,但仍需要在对象间共享信息的场合
-
缺点:属性中包含的引用值始终会在相关对象间共享,跟使用原型模式是一样的
寄生式继承
- 创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,然后返回这个对象
function object (o){
function Fn(){}
Fn.prototype = o;
return new Fn();
}
function createAnother(original){
let clone =object(original); // 通过Object.create创建一个新对象
clone.sayHi = function(){ // 以某种方式增强这个对象
console.log('hi');
}
return clone;
}
let person = {
name: 'Nicholes',
friends: ['Court', 'Van']
}
let anotherPerson = createAnother(person);
console.log(anotherPerson);
anotherPerson.sayHi();
object()
函数不是寄生式继承所必需的,任何返回新对象的函数都可以在这里使用- 缺点:通过寄生式继承给对象添加函数会导致函数难以重用,与构造函数模式类似。
寄生式组合继承
- 即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法;
- 这里只调用了一次
SuperType
构造函数,避免了SubType.prototype
上不必要也用不到的属性,因此可以说这个例子的效率更高。寄生式组合继承可以算是引用类型继承的最佳模式;
function object (o){
function Fn(){}
Fn.prototype = o;
return new Fn();
}
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);
};
let sub = new SubType('Anna', 18);
console.log(sub);
class实现继承
Class
通过extends
关键字实现继承super
在这里作为函数调用,表示父类的构造函数,用来新建父类的this
对象
class SuperType{
constructor(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
sayName(){
console.log(this.name);
}
}
class SubType extends SuperType{
constructor(name, age) {
super(name);
this.age = age;
}
sayAge(){
console.log(this.age);
}
}
let sub = new SubType('Anna', 18);
console.log(sub);
文章参考:
JS中原型式和寄生式继承的详解(代码示例)
js 总结ES6中Class以及继承
JavaScript高级程序设计(第4版)