前端手撕代码(富途)

富途前端面试手撕代码:三道经典题目解析

最近面了富途的前端,主要业务是官网的开发和维护。面试给了三道代码题:打印执行结果、红绿灯交替、买卖股票的最佳时机。

一、宏任务与微任务的执行顺序

题目描述

给定以下代码,预测其输出结果:

javascript 复制代码
console.log('script start');

setTimeout(() => {
  console.log('setTimeout');
}, 0);

async function async1() {
  console.log('async1 start');
  await Promise.resolve().then(() => {
    console.log('promise1');
  });
  console.log('async1 end');
}

async1();

console.log('script end');

思路分析

这道题考察的是 JavaScript 的事件循环机制,尤其是宏任务(macrotask)和微任务(microtask)的执行顺序。JavaScript 的执行机制可以分为以下几个阶段:

  1. 主线程执行:代码从上到下依次执行。
  2. 微任务队列Promise 的回调函数、async/await 等属于微任务,会在当前宏任务执行完后立即处理。
  3. 宏任务队列setTimeoutsetInterval 等属于宏任务,会在当前事件循环的微任务处理完后,进入下一个事件循环时处理。

在代码中:

  • console.log('script start')console.log('script end') 是主线程直接执行的代码,会立即输出。
  • setTimeout 是宏任务,会被放入宏任务队列。
  • async1() 是一个 async 函数,其内部的 await 会将后续代码拆分为微任务。

执行顺序

  1. 主线程先执行 console.log('script start'),输出 script start
  2. 遇到 setTimeout,将其回调函数放入宏任务队列。
  3. 调用 async1(),执行到 console.log('async1 start'),输出 async1 start
  4. 遇到 await,将 Promise.resolve().then(() => { console.log('promise1'); }) 的回调函数放入微任务队列。
  5. await 后的 console.log('async1 end') 会被拆分为微任务,放入微任务队列。
  6. 主线程继续执行 console.log('script end'),输出 script end
  7. 主线程执行完毕后,处理微任务队列:
    • 先执行 Promise.resolve().then() 的回调函数,输出 promise1
    • 然后执行 async1await 后的代码,输出 async1 end
  8. 微任务处理完后,进入下一个事件循环,处理宏任务队列,输出 setTimeout

最终输出结果

sql 复制代码
script start
async1 start
script end
promise1
async1 end
setTimeout

扩展思考

如果代码中有多个微任务和宏任务嵌套,如何预测输出顺序?例如:

javascript 复制代码
console.log('script start');

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

async function async1() {
  console.log('async1 start');
  await Promise.resolve().then(() => {
    console.log('promise1');
  });
  console.log('async1 end');
}

async1();

console.log('script end');

输出结果为:

sql 复制代码
script start
async1 start
script end
promise1
async1 end
setTimeout1
promise2

二、用 asyncPromise 实现红绿灯交替展示

题目描述

实现一个红绿灯交替展示的功能,要求红灯亮 3 秒,绿灯亮 2 秒,黄灯亮 1 秒,循环往复。使用 asyncPromise 实现。

思路分析

红绿灯的交替展示可以通过 async 函数和 Promise 来实现。具体步骤如下:

  1. 使用 PromisesetTimeout 来控制灯的切换时间。
  2. 定义一个状态变量来记录当前灯的状态(红、绿、黄)。
  3. 使用 async 函数和 await 来等待灯的切换。

代码实现

javascript 复制代码
// 定义灯的状态
const RED = 'red';
const GREEN = 'green';
const YELLOW = 'yellow';

// 当前灯的状态
let currentState = RED;

// 灯的容器
const lightContainer = document.getElementById('light-container');

// 更新灯的状态
function updateLight(state) {
  // 清除所有灯的亮状态
  lightContainer.querySelectorAll('.light').forEach(light => {
    light.classList.remove('active');
  });

  // 根据状态点亮对应的灯
  const lightElement = lightContainer.querySelector(`.${state}`);
  if (lightElement) {
    lightElement.classList.add('active');
  }
}

// 创建一个延迟的 Promise
function delay(time) {
  return new Promise(resolve => {
    setTimeout(resolve, time);
  });
}

// 切换灯的状态
async function switchLight() {
  while (true) {
    updateLight(RED);
    await delay(3000); // 红灯亮 3 秒

    updateLight(GREEN);
    await delay(2000); // 绿灯亮 2 秒

    updateLight(YELLOW);
    await delay(1000); // 黄灯亮 1 秒
  }
}

// 初始化
switchLight();

HTML 和 CSS

html 复制代码
<div id="light-container">
  <div class="light red"></div>
  <div class="light yellow"></div>
  <div class="light green"></div>
</div>

<style>
.light-container {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.light {
  width: 50px;
  height: 50px;
  border-radius: 50%;
  background-color: #333;
}

.light.active.red {
  background-color: red;
}

.light.active.yellow {
  background-color: yellow;
}

.light.active.green {
  background-color: green;
}
</style>

扩展思考

如何优化红绿灯的实现?可以考虑以下几点:

  • 使用状态机来管理灯的状态切换。
  • 添加按钮控制红绿灯的暂停和继续。
  • 动态调整灯的亮灭时间。

三、股票最佳买卖时间

题目描述

给定一个数组 prices,其中 prices[i] 表示某股票第 i 天的价格。假设你最多可以完成两笔交易(即买入和卖出一次为一笔交易),设计一个算法来计算你能获得的最大利润。

思路分析

这道题可以通过动态规划来解决。我们需要记录以下四个状态:

  • buy1:第一次买入后的最大利润。
  • sell1:第一次卖出后的最大利润。
  • buy2:第二次买入后的最大利润。
  • sell2:第二次卖出后的最大利润。

具体步骤如下:

  1. 初始化四个状态变量。
  2. 遍历每一天的价格,更新四个状态变量。
  3. 最后返回 sell2,即两次交易后的最大利润。

代码实现

javascript 复制代码
function maxProfit(prices) {
  if (prices.length === 0) return 0;

  // 初始化状态变量
  let buy1 = -prices[0];
  let sell1 = 0;
  let buy2 = -prices[0];
  let sell2 = 0;

  for (let i = 1; i < prices.length; i++) {
    // 更新第一次买入和卖出的状态
    buy1 = Math.max(buy1, -prices[i]);
    sell1 = Math.max(sell1, buy1 + prices[i]);

    // 更新第二次买入和卖出的状态
    buy2 = Math.max(buy2, sell1 - prices[i]);
    sell2 = Math.max(sell2, buy2 + prices[i]);
  }

  return sell2;
}

// 示例
console.log(maxProfit([3,3,5,0,0,3,1,4])); // 输出 6

扩展思考

如果允许最多完成 k 笔交易,如何扩展算法?可以使用二维动态规划:

  • dp[i][j] 表示前 i 天完成 j 笔交易的最大利润。
  • 状态转移方程:
    • dp[i][j] = max(dp[i-1][j], dp[i-1][j-1] + prices[i] - prices[k]),其中 k 是买入的天。

总结

富途前端面试中的三道代码题考的还是很全面,涉及到浏览器事件循环机制、js异步流、leetcode算法题.

相关推荐
yuanbenshidiaos14 分钟前
面试问题总结:qt工程师/c++工程师
c++·qt·面试
半盏茶香19 分钟前
启幕数据结构算法雅航新章,穿梭C++梦幻领域的探索之旅——堆的应用之堆排、Top-K问题
java·开发语言·数据结构·c++·python·算法·链表
uhakadotcom22 分钟前
Langflow:打造AI应用的强大工具
前端·面试·github
uhakadotcom29 分钟前
🤖 LangGraph 多智能体群集
面试·架构·github
小竹子1441 分钟前
L1-1 天梯赛座位分配
数据结构·c++·算法
董董灿是个攻城狮1 小时前
Transformer 通关秘籍8:词向量如何表示近义词?
算法
uhakadotcom1 小时前
Caddy Web服务器初体验:简洁高效的现代选择
前端·面试·github
独好紫罗兰1 小时前
洛谷题单2-P5712 【深基3.例4】Apples-python-流程图重构
开发语言·python·算法
uhakadotcom1 小时前
NVIDIA Resiliency Extension(NVRx)简介:提高PyTorch训练的容错性
算法·面试·github
专业抄代码选手2 小时前
【JS】instanceof 和 typeof 的使用
前端·javascript·面试