资深前端工程师面试题补充 (50题)


十一、JavaScript核心技术深度 (15题)

66. JavaScript执行机制深度理解

题目: 请详细解释JavaScript的事件循环机制,包括宏任务、微任务的执行顺序。

参考答案:

🎯 JavaScript执行机制核心

JavaScript执行机制的本质是单线程异步处理,通过"执行栈→任务队列→事件循环"来实现非阻塞的异步编程模型。

🔄 1. 事件循环基本原理

执行环境组成:

  • 执行栈(Call Stack): 存储函数调用的执行上下文
  • 堆内存(Heap): 存储对象和变量的内存空间
  • 任务队列(Task Queue): 存储待执行的异步任务
  • Web APIs: 浏览器提供的异步API接口

事件循环流程:

  1. 执行同步代码: 按顺序执行主线程上的同步代码
  2. 检查微任务队列: 执行栈清空后,优先处理所有微任务
  3. 执行一个宏任务: 从宏任务队列取出一个任务执行
  4. 重复循环: 继续检查微任务,然后执行下一个宏任务
  5. 渲染更新: 在适当时机进行DOM渲染更新
📋 2. 任务分类详解

宏任务(Macro Task):

  • setTimeout/setInterval: 定时器任务
  • I/O操作: 文件读写、网络请求
  • UI事件: 用户交互事件(click、scroll等)
  • script标签: 整个脚本的执行
  • MessageChannel: 消息通道通信
  • setImmediate: Node.js中的立即执行(IE也支持)

微任务(Micro Task):

  • Promise.then/catch/finally: Promise回调
  • queueMicrotask: 直接添加微任务的API
  • MutationObserver: DOM变化监听
  • process.nextTick: Node.js中的下一个tick(优先级最高)
  • async/await: 本质上是Promise的语法糖
🔍 3. 执行顺序深度分析

基本执行顺序:

console.log('1'); // 同步代码,立即执行

setTimeout(() => {
  console.log('2'); // 宏任务,最后执行
}, 0);

Promise.resolve().then(() => {
  console.log('3'); // 微任务,优先于宏任务
});

console.log('4'); // 同步代码,立即执行

// 输出顺序: 1 → 4 → 3 → 2

复杂嵌套情况:

console.log('start');

setTimeout(() => {
  console.log('timeout1');
  Promise.resolve().then(() => {
    console.log('promise1');
  });
}, 0);

Promise.resolve().then(() => {
  console.log('promise2');
  setTimeout(() => {
    console.log('timeout2');
  }, 0);
});

console.log('end');

// 输出: start → end → promise2 → timeout1 → promise1 → timeout2
4. 微任务优先级机制

微任务执行特点:

  • 批量执行: 一次事件循环中会执行完所有微任务
  • 优先级高: 微任务总是优先于宏任务执行
  • 可能阻塞: 大量微任务可能阻塞宏任务执行
  • 嵌套处理: 微任务中产生的新微任务会在当前循环中执行

Node.js中的特殊情况:

// Node.js中process.nextTick优先级最高
process.nextTick(() => console.log('nextTick'));
Promise.resolve().then(() => console.log('promise'));
setTimeout(() => console.log('timeout'), 0);

// Node.js输出: nextTick → promise → timeout
🎨 5. 渲染时机与优化

浏览器渲染时机:

  • 帧率控制: 浏览器通常以60fps的频率渲染(约16.67ms一帧)
  • 渲染时机: 在宏任务执行完毕后,微任务队列清空后进行渲染
  • 渲染优化: 浏览器会合并多次DOM修改,批量进行渲染
  • 阻塞影响: 长时间运行的JavaScript会阻塞渲染

requestAnimationFrame:

// 在下一次重绘前执行,保证动画流畅
requestAnimationFrame(() => {
  // 动画逻辑
  element.style.transform = 'translateX(100px)';
});

// 比setTimeout更适合动画
setTimeout(() => {
  // 可能在渲染中间执行,导致卡顿
}, 16);
🔧 6. 实际应用与最佳实践

异步编程模式:

// 避免回调地狱
async function fetchData() {
  try {
    const user = await getUserInfo();
    const posts = await getUserPosts(user.id);
    const comments = await getPostComments(posts[0].id);
    return { user, posts, comments };
  } catch (error) {
    console.error('数据获取失败:', error);
  }
}

// 并发处理
async function fetchAllData() {
  const [users, posts, comments] = await Promise.all([
    getUsers(),
    getPosts(), 
    getComments()
  ]);
  return { users, posts, comments };
}

性能优化策略:

// 使用微任务进行批量更新
let pending = false;
const updates = [];

function batchUpdate(callback) {
  updates.push(callback);
  
  if (!pending) {
    pending = true;
    Promise.resolve().then(() => {
      const currentUpdates = updates.splice(0);
      currentUpdates.forEach(update => update());
      pending = false;
    });
  }
}

// 使用方式
batchUpdate(() => updateUI1());
batchUpdate(() => updateUI2());
batchUpdate(() => updateUI3());
// 三个更新会在同一个微任务中批量执行

项目应用: 在大型前端应用中,深入理解事件循环机制对于性能优化至关重要。通过合理利用微任务进行批量更新、使用requestAnimationFrame优化动画、避免长任务阻塞等策略,成功将应用的响应性能提升40%,用户交互延迟降低到50ms以内。关键实践包括:建立性能监控体系、实施任务调度优化、合理使用异步编程模式。

67. 闭包与内存管理

题目: 闭包的实现原理是什么?如何避免闭包导致的内存泄漏?

参考答案:

🎯 闭包核心概念

闭包的本质是词法作用域的持久化,通过"函数+环境→作用域链→变量访问"来实现数据封装和状态保持。

🔍 1. 闭包实现原理

词法作用域:

  • 静态绑定: 函数的作用域在定义时就确定了
  • 作用域链: 内部函数可以访问外部函数的变量
  • 环境记录: JavaScript引擎会保存函数定义时的环境
  • 生命周期延长: 内部函数的引用会延长外部变量的生命周期

闭包形成条件:

  1. 函数嵌套: 内部函数引用外部函数的变量
  2. 函数返回: 内部函数被返回或传递到外部
  3. 变量引用: 外部变量被内部函数持续引用
  4. 执行上下文: 外部函数执行完毕但变量仍被引用
💡 2. 闭包应用场景

数据封装:

function createCounter() {
  let count = 0; // 私有变量
  
  return {
    increment: () => ++count,
    decrement: () => --count,
    getCount: () => count
  };
}

const counter = createCounter();
console.log(counter.getCount()); // 0
counter.increment();
console.log(counter.getCount()); // 1
// count变量无法直接访问,实现了数据封装

模块模式:

const myModule = (function() {
  let privateVar = 0;
  
  function privateMethod() {
    console.log('私有方法');
  }
  
  return {
    publicMethod: function() {
      privateVar++;
      privateMethod();
    },
    getPrivateVar: function() {
      return privateVar;
    }
  };
})();

函数工厂:

function createMultiplier(multiplier) {
  return function(x) {
    return x * multiplier;
  };
}

const double = createMultiplier(2);
const triple = createMultiplier(3);

console.log(double(5)); // 10
console.log(triple(5)); // 15
🐛 3. 内存泄漏问题

常见泄漏场景:

// 问题1: 意外的全局变量
function createHandler() {
  const largeData = new Array(1000000).fill('data');
  
  return function(event) {
    // 只使用event,但largeData无法被回收
    console.log(event.type);
  };
}

// 问题2: DOM节点引用
function attachHandler() {
  const element = document.getElementById('button');
  const largeData = new Array(1000000);
  
  element.onclick = function() {
    // 即使element从DOM中移除,largeData仍被引用
    console.log('clicked');
  };
}

// 问题3: 定时器中的闭包
function startTimer() {
  const largeData = new Array(1000000);
  
  setInterval(function() {
    // largeData被定时器函数引用,无法释放
    console.log('timer tick');
  }, 1000);
}
🔧 4. 内存泄漏解决方案

避免不必要的引用:

// 优化方案1: 只引用需要的变量
function createHandler() {
  const largeData = new Array(1000000).fill('data');
  const neededValue = largeData[0]; // 只保留需要的值
  
  return function(event) {
    console.log(event.type, neededValue);
  };
}

// 优化方案2: 及时清理引用
function createHandlerWithCleanup() {
  let largeData = new Array(1000000).fill('data');
  
  const handler = function(event) {
    console.log(event.type);
  };
  
  // 提供清理方法
  handler.cleanup = function() {
    largeData = null;
  };
  
  return handler;
}

使用WeakMap避免强引用:

const privateData = new WeakMap();

class MyClass {
  constructor(data) {
    // 使用WeakMap存储私有数据
    privateData.set(this, { data });
  }
  
  getData() {
    return privateData.get(this).data;
  }
}

// 当实例被垃圾回收时,WeakMap中的数据也会被清理

合理管理事件监听器:

class Component {
  constructor() {
    this.handlers = [];
    this.handleClick = this.handleClick.bind(this);
  }
  
  addEventListeners() {
    document.addEventListener('click', this.handleClick);
    this.handlers.push(['click', this.handleClick]);
  }
  
  removeEventListeners() {
    this.handlers.forEach(([event, handler]) => {
      document.removeEventListener(event, handler);
    });
    this.handlers = [];
  }
  
  handleClick(event) {
    console.log('clicked');
  }
  
  destroy() {
    this.removeEventListeners();
    // 清理其他引用
  }
}
📊 5. 内存监控与调试

内存使用监控:

function monitorMemoryUsage() {
  if (performance.memory) {
    const { usedJSHeapSize, totalJSHeapSize, jsHeapSizeLimit } = performance.memory;
    
    console.log({
      used: `${(usedJSHeapSize / 1024 / 1024).toFixed(2)} MB`,
      total: `${(totalJSHeapSize / 1024 / 1024).toFixed(2)} MB`,
      limit: `${(jsHeapSizeLimit / 1024 / 1024).toFixed(2)} MB`
    });
  }
}

// 定期监控
setInterval(monitorMemoryUsage, 5000);

闭包调试技巧:

function debugClosure() {
  const data = { value: 42 };
  
  function inner() {
    // 在Chrome DevTools中可以查看Closure作用域
    debugger;
    console.log(data.value);
  }
  
  return inner;
}

const fn = debugClosure();
fn(); // 在debugger处可以查看闭包变量
💡 6. 最佳实践

设计原则:

  • 最小引用: 只引用真正需要的变量
  • 及时清理: 提供清理机制,及时释放资源
  • 避免循环引用: 注意对象间的循环引用
  • 使用弱引用: 适当使用WeakMap、WeakSet

性能优化:

// 使用对象池减少闭包创建
class HandlerPool {
  constructor() {
    this.pool = [];
  }
  
  getHandler(data) {
    let handler = this.pool.pop();
    
    if (!handler) {
      handler = function(event) {
        // 处理逻辑
        console.log(event.type, this.data);
      };
    }
    
    handler.data = data;
    return handler;
  }
  
  releaseHandler(handler) {
    handler.data = null;
    this.pool.push(handler);
  }
}

代码审查要点:

  • 检查是否有不必要的变量引用
  • 确认事件监听器是否正确移除
  • 验证定时器是否正确清理
  • 检查DOM节点引用是否及时释放

项目经验: 在大型SPA应用中,通过系统性的闭包优化和内存管理,成功解决了长时间运行后的内存泄漏问题。关键措施包括:建立内存监控体系、实施闭包最佳实践、定期进行内存分析、建立资源清理机制。最终将应用长时间运行的内存增长控制在合理范围内,显著提升了应用稳定性。

68. 原型链与继承机制

题目: 详细解释JavaScript的原型链机制,以及各种继承方式的优缺点。

参考答案:

🎯 原型链核心概念

JavaScript原型链的本质是对象属性查找机制,通过"对象→原型→原型链→继承"来实现面向对象编程的继承特性。

🔗 1. 原型链基本原理

核心概念:

  • prototype: 函数的原型对象,包含共享的属性和方法
  • proto: 对象的原型引用,指向构造函数的prototype
  • constructor: 原型对象的构造函数引用
  • 原型链: 通过__proto__连接的对象链条

查找机制:

function Person(name) {
  this.name = name;
}

Person.prototype.sayHello = function() {
  console.log(`Hello, I'm ${this.name}`);
};

const person = new Person('Alice');

// 属性查找顺序:
// 1. person.sayHello -> 在person对象上查找
// 2. person.__proto__.sayHello -> 在Person.prototype上查找 ✓
// 3. person.__proto__.__proto__ -> 在Object.prototype上查找
// 4. person.__proto__.__proto__.__proto__ -> null,查找结束
🏗️ 2. 继承方式详解

原型链继承:

function Animal(name) {
  this.name = name;
  this.colors = ['red', 'blue'];
}

Animal.prototype.eat = function() {
  console.log(`${this.name} is eating`);
};

function Dog(name, breed) {
  this.breed = breed;
}

// 原型链继承
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;

const dog1 = new Dog('Buddy', 'Golden');
const dog2 = new Dog('Max', 'Labrador');

// 问题: 引用类型属性被共享
dog1.colors.push('green');
console.log(dog2.colors); // ['red', 'blue', 'green'] - 被污染了

构造函数继承:

function Animal(name) {
  this.name = name;
  this.colors = ['red', 'blue'];
}

Animal.prototype.eat = function() {
  console.log(`${this.name} is eating`);
};

function Dog(name, breed) {
  // 构造函数继承
  Animal.call(this, name);
  this.breed = breed;
}

const dog1 = new Dog('Buddy', 'Golden');
const dog2 = new Dog('Max', 'Labrador');

// 优点: 避免引用类型共享
dog1.colors.push('green');
console.log(dog2.colors); // ['red', 'blue'] - 独立的

// 缺点: 无法继承原型方法
dog1.eat(); // TypeError: dog1.eat is not a function

组合继承:

function Animal(name) {
  this.name = name;
  this.colors = ['red', 'blue'];
}

Animal.prototype.eat = function() {
  console.log(`${this.name} is eating`);
};

function Dog(name, breed) {
  // 构造函数继承 - 继承实例属性
  Animal.call(this, name);
  this.breed = breed;
}

// 原型链继承 - 继承原型方法
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;

Dog.prototype.bark = function() {
  console.log(`${this.name} is barking`);
};

const dog = new Dog('Buddy', 'Golden');
dog.eat(); // 可以调用父类方法
dog.bark(); // 可以调用子类方法

// 缺点: 调用了两次父类构造函数

寄生组合继承:

function Animal(name) {
  this.name = name;
  this.colors = ['red', 'blue'];
}

Animal.prototype.eat = function() {
  console.log(`${this.name} is eating`);
};

function Dog(name, breed) {
  Animal.call(this, name);
  this.breed = breed;
}

// 寄生组合继承 - 最佳方案
function inheritPrototype(child, parent) {
  const prototype = Object.create(parent.prototype);
  prototype.constructor = child;
  child.prototype = prototype;
}

inheritPrototype(Dog, Animal);

Dog.prototype.bark = function() {
  console.log(`${this.name} is barking`);
};

// 优点: 只调用一次父类构造函数,效率最高
🆕 3. ES6 Class继承

Class语法:

class Animal {
  constructor(name) {
    this.name = name;
    this.colors = ['red', 'blue'];
  }
  
  eat() {
    console.log(`${this.name} is eating`);
  }
  
  static getSpecies() {
    return 'Animal';
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name); // 调用父类构造函数
    this.breed = breed;
  }
  
  bark() {
    console.log(`${this.name} is barking`);
  }
  
  eat() {
    super.eat(); // 调用父类方法
    console.log('Dog finished eating');
  }
}

const dog = new Dog('Buddy', 'Golden');
dog.eat(); // 可以调用父类和子类方法

Class vs 函数构造器对比:

// 函数构造器
function Person(name) {
  this.name = name;
}
Person.prototype.greet = function() {
  console.log(`Hello, ${this.name}`);
};

// Class语法 - 本质上是语法糖
class PersonClass {
  constructor(name) {
    this.name = name;
  }
  
  greet() {
    console.log(`Hello, ${this.name}`);
  }
}

// 两者创建的对象在原型链上是相同的
console.log(PersonClass.prototype.constructor === PersonClass); // true
🔧 4. 高级继承技巧

Mixin模式:

// 定义Mixin
const Flyable = {
  fly() {
    console.log(`${this.name} is flying`);
  }
};

const Swimmable = {
  swim() {
    console.log(`${this.name} is swimming`);
  }
};

// 应用Mixin
class Duck extends Animal {
  constructor(name) {
    super(name);
  }
}

// 混入多个能力
Object.assign(Duck.prototype, Flyable, Swimmable);

const duck = new Duck('Donald');
duck.fly(); // Donald is flying
duck.swim(); // Donald is swimming

工厂函数模式:

function createAnimal(name, type) {
  const animal = {
    name,
    type,
    eat() {
      console.log(`${this.name} is eating`);
    }
  };
  
  // 根据类型添加特定方法
  if (type === 'bird') {
    animal.fly = function() {
      console.log(`${this.name} is flying`);
    };
  } else if (type === 'fish') {
    animal.swim = function() {
      console.log(`${this.name} is swimming`);
    };
  }
  
  return animal;
}

const bird = createAnimal('Tweety', 'bird');
const fish = createAnimal('Nemo', 'fish');
🛠️ 5. 原型操作方法

Object.create():

// 创建具有指定原型的对象
const animalPrototype = {
  eat() {
    console.log('eating...');
  }
};

const dog = Object.create(animalPrototype);
dog.name = 'Buddy';
dog.eat(); // eating...

// 等价于
const dog2 = {};
dog2.__proto__ = animalPrototype;

Object.setPrototypeOf():

const animal = {
  eat() {
    console.log('eating...');
  }
};

const dog = {
  name: 'Buddy'
};

// 设置原型
Object.setPrototypeOf(dog, animal);
dog.eat(); // eating...

hasOwnProperty()和in操作符:

function Person(name) {
  this.name = name;
}

Person.prototype.age = 25;

const person = new Person('Alice');

console.log('name' in person); // true
console.log('age' in person); // true
console.log(person.hasOwnProperty('name')); // true
console.log(person.hasOwnProperty('age')); // false
📊 6. 性能考虑

原型链长度影响:

// 避免过长的原型链
class A {}
class B extends A {}
class C extends B {}
class D extends C {}
class E extends D {} // 原型链过长,影响属性查找性能

// 推荐: 保持合理的继承层次

属性访问优化:

// 频繁访问的属性应该在实例上
class OptimizedClass {
  constructor() {
    // 将常用方法绑定到实例
    this.frequentMethod = this.frequentMethod.bind(this);
  }
  
  frequentMethod() {
    // 频繁调用的方法
  }
  
  rareMethod() {
    // 不常用的方法保持在原型上
  }
}
💡 7. 最佳实践

继承设计原则:

  • 优先组合: 优先使用组合而非继承
  • 浅层继承: 保持继承层次的简单
  • 接口隔离: 使用Mixin实现多重继承
  • 里氏替换: 子类应该能够替换父类

现代JavaScript推荐:

// 推荐: 使用Class语法
class Animal {
  constructor(name) {
    this.name = name;
  }
  
  eat() {
    console.log(`${this.name} is eating`);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }
  
  bark() {
    console.log(`${this.name} is barking`);
  }
}

// 或者使用工厂函数 + 组合
function createDog(name, breed) {
  return {
    name,
    breed,
    eat() {
      console.log(`${this.name} is eating`);
    },
    bark() {
      console.log(`${this.name} is barking`);
    }
  };
}

项目经验: 在大型项目中,合理的继承设计对代码维护性至关重要。通过采用ES6 Class语法结合Mixin模式,成功构建了灵活的组件继承体系。关键实践包括:保持浅层继承结构、使用组合优于继承的设计原则、建立清晰的接口规范、实施严格的代码审查机制。最终实现了代码复用率提升30%,维护成本降低40%的效果。

69. 异步编程深度解析

题目: 从回调函数到Promise再到async/await,详细分析JavaScript异步编程的演进和最佳实践。

参考答案:

🎯 异步编程核心价值

JavaScript异步编程的本质是非阻塞执行,通过"回调→Promise→async/await→并发控制"的演进来解决复杂的异步操作管理问题。

📈 1. 异步编程演进历程

回调函数时代:

// 回调地狱的典型例子
function getUserData(userId, callback) {
  setTimeout(() => {
    console.log('获取用户信息');
    callback(null, { id: userId, name: 'Alice' });
  }, 1000);
}

function getUserPosts(userId, callback) {
  setTimeout(() => {
    console.log('获取用户文章');
    callback(null, [{ id: 1, title: 'Post 1' }]);
  }, 1000);
}

function getPostComments(postId, callback) {
  setTimeout(() => {
    console.log('获取文章评论');
    callback(null, [{ id: 1, content: 'Great post!' }]);
  }, 1000);
}

// 回调地狱
getUserData(1, (err, user) => {
  if (err) return console.error(err);
  
  getUserPosts(user.id, (err, posts) => {
    if (err) return console.error(err);
    
    getPostComments(posts[0].id, (err, comments) => {
      if (err) return console.error(err);
      
      console.log('完成所有数据获取', { user, posts, comments });
    });
  });
});

Promise时代:

// Promise化改造
function getUserDataPromise(userId) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('获取用户信息');
      resolve({ id: userId, name: 'Alice' });
    }, 1000);
  });
}

function getUserPostsPromise(userId) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('获取用户文章');
      resolve([{ id: 1, title: 'Post 1' }]);
    }, 1000);
  });
}

function getPostCommentsPromise(postId) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('获取文章评论');
      resolve([{ id: 1, content: 'Great post!' }]);
    }, 1000);
  });
}

// Promise链式调用
getUserDataPromise(1)
  .then(user => {
    return getUserPostsPromise(user.id)
      .then(posts => ({ user, posts }));
  })
  .then(({ user, posts }) => {
    return getPostCommentsPromise(posts[0].id)
      .then(comments => ({ user, posts, comments }));
  })
  .then(result => {
    console.log('完成所有数据获取', result);
  })
  .catch(error => {
    console.error('发生错误:', error);
  });

async/await时代:

// async/await优雅解决方案
async function fetchAllData(userId) {
  try {
    console.log('开始获取数据...');
    
    const user = await getUserDataPromise(userId);
    const posts = await getUserPostsPromise(user.id);
    const comments = await getPostCommentsPromise(posts[0].id);
    
    console.log('完成所有数据获取', { user, posts, comments });
    return { user, posts, comments };
  } catch (error) {
    console.error('数据获取失败:', error);
    throw error;
  }
}

// 使用
fetchAllData(1);
2. 并发控制与优化

并行执行:

// 串行执行 - 耗时3秒
async function serialExecution() {
  const start = Date.now();
  
  const user = await getUserDataPromise(1);
  const posts = await getUserPostsPromise(1);
  const comments = await getPostCommentsPromise(1);
  
  console.log(`串行执行耗时: ${Date.now() - start}ms`);
  return { user, posts, comments };
}

// 并行执行 - 耗时1秒
async function parallelExecution() {
  const start = Date.now();
  
  const [user, posts, comments] = await Promise.all([
    getUserDataPromise(1),
    getUserPostsPromise(1),
    getPostCommentsPromise(1)
  ]);
  
  console.log(`并行执行耗时: ${Date.now() - start}ms`);
  return { user, posts, comments };
}

错误处理策略:

// Promise.allSettled - 处理部分失败
async function robustDataFetch() {
  const results = await Promise.allSettled([
    getUserDataPromise(1),
    getUserPostsPromise(1),
    getPostCommentsPromise(1)
  ]);
  
  const successResults = results
    .filter(result => result.status === 'fulfilled')
    .map(result => result.value);
    
  const errors = results
    .filter(result => result.status === 'rejected')
    .map(result => result.reason);
  
  if (errors.length > 0) {
    console.warn('部分请求失败:', errors);
  }
  
  return successResults;
}

// 重试机制
async function fetchWithRetry(fn, maxRetries = 3, delay = 1000) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      console.log(`${i + 1}次尝试失败:`, error.message);
      
      if (i === maxRetries - 1) {
        throw error;
      }
      
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}
🔄 3. 高级异步模式

生成器与异步迭代:

// 异步生成器
async function* asyncGenerator() {
  let i = 0;
  while (i < 5) {
    await new Promise(resolve => setTimeout(resolve, 1000));
    yield i++;
  }
}

// 使用异步迭代
async function consumeAsyncGenerator() {
  for await (const value of asyncGenerator()) {
    console.log('生成器产生值:', value);
  }
}

// 自定义异步迭代器
class AsyncRange {
  constructor(start, end) {
    this.start = start;
    this.end = end;
  }
  
  async *[Symbol.asyncIterator]() {
    for (let i = this.start; i <= this.end; i++) {
      await new Promise(resolve => setTimeout(resolve, 500));
      yield i;
    }
  }
}

// 使用
async function useAsyncRange() {
  for await (const num of new AsyncRange(1, 5)) {
    console.log('异步范围值:', num);
  }
}

流式处理:

// 异步数据流处理
class AsyncDataStream {
  constructor(source) {
    this.source = source;
  }
  
  async *map(fn) {
    for await (const item of this.source) {
      yield await fn(item);
    }
  }
  
  async *filter(predicate) {
    for await (const item of this.source) {
      if (await predicate(item)) {
        yield item;
      }
    }
  }
  
  async collect() {
    const results = [];
    for await (const item of this.source) {
      results.push(item);
    }
    return results;
  }
}

// 使用流式处理
async function processDataStream() {
  const numbers = async function*() {
    for (let i = 1; i <= 10; i++) {
      await new Promise(resolve => setTimeout(resolve, 100));
      yield i;
    }
  };
  
  const stream = new AsyncDataStream(numbers())
    .filter(async x => x % 2 === 0)
    .map(async x => x * 2);
  
  const results = await stream.collect();
  console.log('流处理结果:', results); // [4, 8, 12, 16, 20]
}
🎛️ 4. 并发控制机制

限制并发数量:

class ConcurrencyController {
  constructor(limit) {
    this.limit = limit;
    this.running = 0;
    this.queue = [];
  }
  
  async execute(asyncFn) {
    return new Promise((resolve, reject) => {
      this.queue.push({
        asyncFn,
        resolve,
        reject
      });
      
      this.process();
    });
  }
  
  async process() {
    if (this.running >= this.limit || this.queue.length === 0) {
      return;
    }
    
    this.running++;
    const { asyncFn, resolve, reject } = this.queue.shift();
    
    try {
      const result = await asyncFn();
      resolve(result);
    } catch (error) {
      reject(error);
    } finally {
      this.running--;
      this.process();
    }
  }
}

// 使用并发控制
async function controlledConcurrency() {
  const controller = new ConcurrencyController(3); // 最多3个并发
  
  const tasks = Array.from({ length: 10 }, (_, i) => 
    () => new Promise(resolve => 
      setTimeout(() => resolve(`任务${i + 1}完成`), Math.random() * 2000)
    )
  );
  
  const results = await Promise.all(
    tasks.map(task => controller.execute(task))
  );
  
  console.log('所有任务完成:', results);
}

信号量控制:

class Semaphore {
  constructor(count) {
    this.count = count;
    this.waiting = [];
  }
  
  async acquire() {
    if (this.count > 0) {
      this.count--;
      return;
    }
    
    return new Promise(resolve => {
      this.waiting.push(resolve);
    });
  }
  
  release() {
    if (this.waiting.length > 0) {
      const resolve = this.waiting.shift();
      resolve();
    } else {
      this.count++;
    }
  }
  
  async execute(asyncFn) {
    await this.acquire();
    try {
      return await asyncFn();
    } finally {
      this.release();
    }
  }
}

// 使用信号量
const semaphore = new Semaphore(2);

async function limitedResourceAccess() {
  const tasks = Array.from({ length: 5 }, (_, i) => 
    semaphore.execute(async () => {
      console.log(`任务${i + 1}开始执行`);
      await new Promise(resolve => setTimeout(resolve, 2000));
      console.log(`任务${i + 1}执行完成`);
      return `结果${i + 1}`;
    })
  );
  
  const results = await Promise.all(tasks);
  console.log('所有任务结果:', results);
}
🔧 5. 实际应用场景

API请求管理:

class APIManager {
  constructor() {
    this.cache = new Map();
    this.pendingRequests = new Map();
  }
  
  async request(url, options = {}) {
    // 缓存检查
    if (this.cache.has(url)) {
      return this.cache.get(url);
    }
    
    // 防止重复请求
    if (this.pendingRequests.has(url)) {
      return this.pendingRequests.get(url);
    }
    
    // 发起请求
    const requestPromise = this.fetchData(url, options);
    this.pendingRequests.set(url, requestPromise);
    
    try {
      const result = await requestPromise;
      this.cache.set(url, result);
      return result;
    } finally {
      this.pendingRequests.delete(url);
    }
  }
  
  async fetchData(url, options) {
    const response = await fetch(url, options);
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }
    return response.json();
  }
}

批量操作优化:

class BatchProcessor {
  constructor(batchSize = 10, delay = 100) {
    this.batchSize = batchSize;
    this.delay = delay;
    this.queue = [];
    this.processing = false;
  }
  
  async add(item) {
    return new Promise((resolve, reject) => {
      this.queue.push({ item, resolve, reject });
      this.scheduleProcess();
    });
  }
  
  scheduleProcess() {
    if (!this.processing) {
      this.processing = true;
      setTimeout(() => this.process(), this.delay);
    }
  }
  
  async process() {
    while (this.queue.length > 0) {
      const batch = this.queue.splice(0, this.batchSize);
      
      try {
        const results = await this.processBatch(
          batch.map(item => item.item)
        );
        
        batch.forEach((item, index) => {
          item.resolve(results[index]);
        });
      } catch (error) {
        batch.forEach(item => {
          item.reject(error);
        });
      }
    }
    
    this.processing = false;
  }
  
  async processBatch(items) {
    // 批量处理逻辑
    console.log(`批量处理 ${items.length} 个项目`);
    await new Promise(resolve => setTimeout(resolve, 1000));
    return items.map(item => `处理结果: ${item}`);
  }
}

项目经验: 在大型前端应用中,合理的异步编程模式对性能和用户体验至关重要。通过采用async/await结合并发控制、实施请求去重和缓存机制、建立错误重试和降级策略,成功将接口响应时间优化50%,用户体验显著提升。关键实践包括:建立统一的异步操作规范、实施细粒度的并发控制、完善的错误处理机制、性能监控和优化。

祝您面试顺利!


<think>好的,用户需要资深前端开发工程师面试题及答案,涉及高级技术、框架使用和最佳实践。首先,我得整理常见的高级问,比如框架原理、性能优化、设计模式等。然后,结合用户提供的引用内容,比如WebWorker、Flexbox属性和盒子模型,可能需要扩展相关的高级问。例如,Web Worker在性能优化中的应用,Flex布局的深入问,或者盒子模型的高级处理技巧。还要考虑用户之前的问,可能涉及面试题的结构和深度。需要确保回答不仅涵盖基础,还要深入原理和实践,比如Vue/React的响应式机制、虚拟DOM的diff算法等。另外,引用中的项目属性如flex-grow、flex-shrink,可以设计相关问考察候选人对Flex布局的掌握程度。盒子模型的问需要提到box-sizing的高级应用场景。还要注意生成的问要具有挑战性,适合资深岗位,比如框架源码理解、大型项目优化策略、状态管理方案比较等。每个问需附上答案,并确保答案准确,引用相关知识点,如引用中的Web Worker和Flex属性。 最后,检查是否符合格式要求,使用正确的中文表达,数学公式用$...$,独立公式用$$...$$,但当前内容可能较少涉及数学公式,需留意。确保LaTeX语法正确,结构清晰,问与答案分开,引用标识自然添加。</think>### 资深前端开发工程师常见面试题及答案 #### 1. 框架原理与优化 **问:Vue/React 的响应式系统如何实现?** 答案: - Vue 通过`Object.defineProperty`(Vue 2)或`Proxy`(Vue 3)劫持数据变化,触发依赖更新[^1]。 - React 依赖`setState`显式触发组件重新渲染,结合虚拟 DOM 的 diff 算法优化更新效率[^2]。 **优化方案**:减少不必要的渲染(如`React.memo`)、按需加载组件、合理使用状态管理工具。 --- #### 2. 性能优化实践 **问:如何优化首屏加载时间?** 答案: - 代码分割(Code Splitting)与懒加载(如`React.lazy`) - 静态资源压缩(Webpack 的`TerserPlugin`) - 服务端渲染(SSR)或静态站点生成(SSG) - 利用浏览器缓存策略(强缓存与协商缓存) 引用案例:Web Worker 可处理复杂计算,避免主线程阻塞[^1]。 --- #### 3. 设计模式与工程化 **问前端前端架构的落地难点是什么?** 答案: - 子应用隔离(CSS/JS 沙箱) - 通信机制(自定义事件或状态共享) - 构建工具链统一(如 Module Federation) - 性能监控与错误追踪 --- #### 4. 高级 CSS 实践 **问:如何实现一个自适应的 Flex 布局容器?** 答案: ```css .container { display: flex; flex-wrap: wrap; justify-content: space-between; } .item { flex: 1 1 200px; /* flex-grow:1, flex-shrink:1, flex-basis:200px */ } ``` 关键属性:`flex-grow`控制放大比例,`flex-shrink`控制缩小比例[^2]。 --- #### 5. 浏览器与网络 **问:从输入 URL 到页面渲染的完整过程是什么?** 答案: - DNS 解析 → TCP 连接 → HTTP 请求 - 服务器响应 → 解析 HTML/CSS/JS → 构建 DOM/CSSOM 树 - 渲染树(Render Tree)生成 → 布局(Layout) → 绘制(Paint) **性能瓶颈**:重排(Reflow)和重绘(Repaint)需尽量减少。 --- #### 6. 状态管理方案 **问:Redux 与 Vuex 的核心差异是什么?** 答案: - Redux 强调单一数据源和纯函数(Reducer),需搭配中间件(如 Redux-Thunk)处理异步。 - Vuex 深度集成 Vue,通过`mutations`同步修改状态,`actions`处理异步逻辑。 **最佳实践**:大型项目推荐使用状态管理,小型项目可依赖组件局部状态。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值