面试题答案
一键面试原型链导致性能瓶颈的情况及避免方法
- 频繁的原型链查找
- 情况分析:当通过原型链查找属性或方法时,如果原型链很长,每次查找都需要遍历整个原型链,这会导致性能下降。例如,在如下代码中:
function Animal() {}
Animal.prototype.speak = function() {
console.log('I am an animal');
};
function Dog() {}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
function Poodle() {}
Poodle.prototype = Object.create(Dog.prototype);
Poodle.prototype.constructor = Poodle;
let poodle = new Poodle();
// 当调用poodle.speak()时,需要从Poodle.prototype -> Dog.prototype -> Animal.prototype查找speak方法
poodle.speak();
- **避免方法**:尽量减少原型链的深度,将常用的方法直接定义在实例对象或更接近实例对象的原型上。例如,可以在`Dog`原型上直接定义一些`Poodle`常用的方法,而不是都依赖于`Animal`原型。
2. 在循环中访问原型属性 - 情况分析:在循环内部频繁访问原型属性,每次访问都会触发原型链查找,导致性能问题。如下代码:
function Person() {}
Person.prototype.age = 30;
let person = new Person();
for (let i = 0; i < 1000000; i++) {
// 每次循环都访问原型属性age
console.log(person.age);
}
- **避免方法**:将原型属性缓存到局部变量中,在循环外部访问一次原型属性,然后在循环内部使用局部变量。
function Person() {}
Person.prototype.age = 30;
let person = new Person();
let age = person.age;
for (let i = 0; i < 1000000; i++) {
console.log(age);
}
__proto__
和 prototype
在复杂继承结构中的混淆及正确使用
- 混淆点
__proto__
是每个对象(除了null
)都有的属性,它指向该对象的原型对象。而prototype
是函数对象特有的属性,当一个函数被用作构造函数时,prototype
属性会成为由该构造函数创建的实例的原型对象。在复杂继承结构中,容易混淆两者的作用和使用场景。例如,在多重继承的情况下,开发者可能会错误地在实例对象上直接修改prototype
,或者错误地使用__proto__
来定义继承关系。
- 正确使用方法
- 使用
prototype
定义继承关系:通过设置构造函数的prototype
属性来定义继承结构。例如:
- 使用
function Shape() {}
Shape.prototype.draw = function() {
console.log('Drawing a shape');
};
function Circle() {}
Circle.prototype = Object.create(Shape.prototype);
Circle.prototype.constructor = Circle;
let circle = new Circle();
circle.draw();
- **使用`__proto__`理解原型链**:`__proto__`可以用来查看对象的原型链。例如:
console.log(circle.__proto__ === Circle.prototype); // true
console.log(circle.__proto__.__proto__ === Shape.prototype); // true
在实际项目中,保持清晰的继承结构,明确prototype
用于定义继承关系,__proto__
用于查看和理解原型链,可以避免潜在错误并优化代码结构。