面试题答案
一键面试面临的挑战
- 内存占用过多:
- 类实例属性重复:在JavaScript中,通过类创建对象时,如果每个实例都有相同的属性(即使这些属性的值相同),每个实例都会为这些属性分配独立的内存空间。例如:
class Person { constructor(name) { this.name = name; this.species = 'human'; // 每个实例都有相同的species属性,浪费内存 } }
- 原型链增长:随着继承层次的加深,原型链会变长。每个对象的
__proto__
指针都会占用一定的内存,并且查找属性时沿着原型链遍历也会增加开销。比如多层继承:
这里class Animal {} class Mammal extends Animal {} class Dog extends Mammal {} let myDog = new Dog();
myDog
的原型链就有三层,增加了内存占用和查找成本。 - 内存泄漏风险:
- 循环引用:在对象之间形成循环引用时,如果没有正确处理,可能导致内存无法释放。例如:
function createCircularReference() { let obj1 = {}; let obj2 = {}; obj1.ref = obj2; obj2.ref = obj1; return {obj1, obj2}; } let result = createCircularReference(); // 这里obj1和obj2形成循环引用,如果没有外部引用断开,它们占用的内存无法释放
- 事件监听器未移除:如果在对象的原型上添加了事件监听器,但在对象不再使用时没有移除这些监听器,会导致对象一直被引用,无法被垃圾回收机制回收。例如:
class MyElement { constructor() { document.addEventListener('click', this.handleClick.bind(this)); } handleClick() { console.log('Clicked'); } } let element = new MyElement(); // 如果element不再使用,但事件监听器未移除,element及其相关内存无法释放
优化措施
- 优化内存占用:
- 共享原型属性:对于类中不会因实例不同而变化的属性和方法,应定义在原型上。例如:
class Person { constructor(name) { this.name = name; } sayHello() { console.log(`Hello, I'm ${this.name}`); } } // sayHello方法定义在原型上,所有Person实例共享,节省内存
- 减少继承层次:尽量简化继承结构,避免不必要的多层继承。如果可能,使用组合模式代替继承,将功能组合到对象中,而不是通过继承来获取。例如:
class Logger { log(message) { console.log(message); } } class MyClass { constructor() { this.logger = new Logger(); } doSomething() { this.logger.log('Doing something'); } }
- 避免内存泄漏:
- 打破循环引用:在不再需要循环引用的对象时,手动打破引用。例如上述循环引用的例子,可以这样处理:
function createCircularReference() { let obj1 = {}; let obj2 = {}; obj1.ref = obj2; obj2.ref = obj1; return {obj1, obj2}; } let result = createCircularReference(); // 不再使用时打破循环引用 result.obj1.ref = null; result.obj2.ref = null;
- 移除事件监听器:在对象销毁前,移除添加到原型或实例上的事件监听器。例如:
class MyElement { constructor() { this.handleClick = this.handleClick.bind(this); document.addEventListener('click', this.handleClick); } handleClick() { console.log('Clicked'); } destroy() { document.removeEventListener('click', this.handleClick); } } let element = new MyElement(); // 当element不再使用时 element.destroy();