面试题答案
一键面试关键要点
- 原型链的构建:通过将子类的原型对象设置为父类的实例,从而形成原型链,使得子类可以访问父类原型上的属性和方法。
- 借用构造函数:在子类构造函数内部通过
call
或apply
方法调用父类构造函数,以继承父类的实例属性。
不同继承方式及代码示例
- 原型链继承
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(this.name +'makes a sound.');
};
function Dog(name, breed) {
this.breed = breed;
}
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;
let myDog = new Dog('Buddy', 'Golden Retriever');
myDog.speak();
优点:简单直观,易于理解和实现,子类可以共享父类原型上的属性和方法,节省内存。 缺点:无法向父类构造函数传递参数;所有子类实例共享父类实例属性,一个实例对该属性的修改会影响其他实例。
- 借用构造函数继承
function Animal(name) {
this.name = name;
}
function Dog(name, breed) {
Animal.call(this, name);
this.breed = breed;
}
let myDog = new Dog('Buddy', 'Golden Retriever');
console.log(myDog.name);
优点:可以向父类构造函数传递参数,每个子类实例都有自己独立的属性,不会相互影响。 缺点:子类无法继承父类原型上的方法,必须在每个子类构造函数中重新定义相同的方法,导致代码冗余。
- 组合继承
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(this.name +'makes a sound.');
};
function Dog(name, breed) {
Animal.call(this, name);
this.breed = breed;
}
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;
let myDog = new Dog('Buddy', 'Golden Retriever');
myDog.speak();
优点:融合了原型链继承和借用构造函数继承的优点,既能继承父类原型上的方法,又能拥有自己独立的属性。
缺点:父类构造函数会被调用两次,一次是在创建子类原型时(new Animal()
),一次是在子类构造函数内部(Animal.call(this, name)
),导致不必要的开销。
- 寄生组合继承
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(this.name +'makes a sound.');
};
function Dog(name, breed) {
Animal.call(this, name);
this.breed = breed;
}
function inheritPrototype(subType, superType) {
let prototype = Object.create(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}
inheritPrototype(Dog, Animal);
let myDog = new Dog('Buddy', 'Golden Retriever');
myDog.speak();
优点:修复了组合继承中父类构造函数被调用两次的问题,高效地实现了继承。既保证了原型链的正确性,又能正常向父类构造函数传递参数,同时避免了不必要的开销。 缺点:实现相对复杂,需要额外的辅助函数来处理原型链的构建。