面试题答案
一键面试原型链继承
- 实现方式:
function Parent() { this.value = 1; } Parent.prototype.getValue = function() { return this.value; }; function Child() {} Child.prototype = new Parent(); Child.prototype.constructor = Child; let child = new Child(); console.log(child.getValue());
- 优点:
- 非常纯粹的继承关系,子类可以访问到父类原型上的所有属性和方法。
- 实现简单,易于理解。
- 缺点:
- 所有子类实例共享父类实例的属性,若父类属性为引用类型,一个子类实例对其修改会影响其他子类实例。例如,若
Parent
中有一个数组属性,一个Child
实例向该数组添加元素,其他Child
实例访问该数组时也会看到新增元素。 - 创建子类实例时,无法向父类构造函数传参。
- 所有子类实例共享父类实例的属性,若父类属性为引用类型,一个子类实例对其修改会影响其他子类实例。例如,若
构造函数继承
- 实现方式:
function Parent(name) { this.name = name; this.colors = ['red', 'blue']; } function Child(name, age) { Parent.call(this, name); this.age = age; } let child = new Child('John', 20); console.log(child.name); console.log(child.age);
- 优点:
- 解决了子类实例共享父类实例属性的问题,每个子类实例都有自己独立的属性。
- 可以在创建子类实例时向父类构造函数传递参数。
- 缺点:
- 子类无法访问父类原型上的方法,只能访问父类构造函数内部定义的属性和方法,代码复用性差。
组合继承
- 实现方式:
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('John', 20); child.colors.push('green'); console.log(child.colors); child.sayName();
- 优点:
- 融合了原型链继承和构造函数继承的优点,既实现了函数复用(可以访问父类原型方法),又保证了每个子类实例有自己独立的属性。
- 缺点:
- 父类构造函数会被调用两次,一次是在
Child.prototype = new Parent()
,另一次是在Parent.call(this, name)
中,造成了不必要的性能开销。
- 父类构造函数会被调用两次,一次是在
寄生组合继承
- 实现方式:
function inheritPrototype(Child, Parent) { let prototype = Object.create(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('John', 20); child.colors.push('green'); console.log(child.colors); child.sayName();
- 优点:
- 解决了组合继承中父类构造函数被调用两次的问题,只调用一次父类构造函数,提高了性能。
- 同时保留了组合继承的优点,既实现了函数复用,又保证子类实例有独立属性。
- 缺点:
- 实现相对复杂,需要额外的辅助函数(如
inheritPrototype
)来处理原型继承关系。
- 实现相对复杂,需要额外的辅助函数(如
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('John', 20); child.colors.push('green'); console.log(child.colors); child.sayName();
- 优点:
- 语法更加简洁、清晰,更符合面向对象编程的习惯。
- 实现了原型链继承和构造函数继承的合理融合,避免了父类构造函数多次调用的问题。
- 类的定义和继承结构一目了然,易于维护和理解。
- 缺点:
- 兼容性不如传统继承方式,在一些老旧浏览器中可能需要使用转译工具(如 Babel)进行转换才能正常运行。