前端手撕代码(富途)

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

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

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

题目描述

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

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算法题.

相关推荐
前端小白佬2 分钟前
【JS】事件传播--事件捕获/冒泡
javascript·面试
itsuifengerxing10 分钟前
python 自定义无符号右移
算法
汪子熙19 分钟前
深入解析互斥锁(Mutex):并发编程中的关键同步机制
后端·面试
猎板PCB厚铜专家大族40 分钟前
高频 PCB 技术发展趋势与应用解析
人工智能·算法·设计规范
dying_man1 小时前
LeetCode--24.两两交换链表中的结点
算法·leetcode
yours_Gabriel1 小时前
【力扣】2434.使用机器人打印字典序最小的字符串
算法·leetcode·贪心算法
草莓熊Lotso2 小时前
【数据结构初阶】--算法复杂度的深度解析
c语言·开发语言·数据结构·经验分享·笔记·其他·算法
KyollBM2 小时前
【CF】Day75——CF (Div. 2) B (数学 + 贪心) + CF 882 (Div. 2) C (01Trie | 区间最大异或和)
c语言·c++·算法
CV点灯大师2 小时前
C++算法训练营 Day10 栈与队列(1)
c++·redis·算法
GGBondlctrl2 小时前
【leetcode】递归,回溯思想 + 巧妙解法-解决“N皇后”,以及“解数独”题目
算法·leetcode·n皇后·有效的数独·解数独·映射思想·数学思想