面试题答案
一键面试原型链继承
实现方式:
function Parent() {
this.name = 'parent';
}
Parent.prototype.sayName = function() {
console.log(this.name);
};
function Child() {}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
let child = new Child();
child.sayName();
优点:
- 非常纯粹的继承关系,通过原型链实现,结构清晰。
- 父类新增原型方法/原型属性,子类都能访问到。
缺点:
- 所有子类实例共享父类的引用类型属性,一个实例对引用类型属性的修改会影响其他实例。
- 创建子类实例时,无法向父类构造函数传参。
构造函数继承(经典继承)
实现方式:
function Parent(name) {
this.name = name;
this.colors = ['red', 'blue'];
}
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
let child1 = new Child('child1', 5);
let child2 = new Child('child2', 6);
child1.colors.push('green');
console.log(child1.colors);
console.log(child2.colors);
优点:
- 可以在子类构造函数中向父类构造函数传参。
- 解决了子类实例共享父类引用类型属性的问题。
缺点:
- 只能继承父类的实例属性和方法,不能继承原型属性/方法。
- 每创建一个子类实例,父类构造函数都会被调用一次,造成性能开销。
组合继承(伪经典继承)
实现方式:
function Parent(name) {
this.name = name;
this.colors = ['red', 'blue'];
}
Parent.prototype.sayName = function() {
console.log(this.name);
};
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
let child = new Child('child', 5);
child.sayName();
优点:
- 融合了原型链继承和构造函数继承的优点,既能继承实例属性和方法,又能继承原型属性和方法。
- 可以向父类构造函数传参。
缺点:
- 调用了两次父类构造函数,一次在
Child.prototype = new Parent()
,一次在Parent.call(this, name)
,有一定性能浪费。
寄生组合式继承
实现方式:
function createObject(o) {
function F() {}
F.prototype = o;
return new F();
}
function inheritPrototype(Child, Parent) {
let prototype = createObject(Parent.prototype);
prototype.constructor = Child;
Child.prototype = prototype;
}
function Parent(name) {
this.name = name;
this.colors = ['red', 'blue'];
}
Parent.prototype.sayName = function() {
console.log(this.name);
};
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
inheritPrototype(Child, Parent);
let child = new Child('child', 5);
child.sayName();
优点:
- 只调用一次父类构造函数,避免了组合继承中两次调用父类构造函数的性能浪费。
- 完美实现继承,既继承实例属性和方法,又继承原型属性和方法。
缺点:
- 实现相对复杂,需要额外的辅助函数。
ES6 class 继承
实现方式:
class Parent {
constructor(name) {
this.name = name;
this.colors = ['red', 'blue'];
}
sayName() {
console.log(this.name);
}
}
class Child extends Parent {
constructor(name, age) {
super(name);
this.age = age;
}
}
let child = new Child('child', 5);
child.sayName();
优点:
- 语法更加简洁、清晰,易于理解和维护。
- 融合了原型链继承和构造函数继承的优点,既能继承实例属性和方法,又能继承原型属性和方法。
- 可以方便地使用
super
关键字调用父类的构造函数和方法。
缺点:
- 本质上还是基于原型链和构造函数的继承,并没有完全摆脱传统继承方式的一些固有问题。
- 对于不支持ES6的环境,需要进行转码。