前端八股文面经大全:腾讯前端暑期提前批一、二、三面面经(下)(2026-03-04)·面经深度解析

前言

大家好,我是木斯佳。

相信很多人都感受到了,在AI浪潮的席卷之下,前端领域的门槛在变高,纯粹的"增删改查"岗位正在肉眼可见地减少。曾经热闹非凡的面经分享,如今也沉寂了许多。但我们都知道,市场的潮水退去,留下的才是真正在踏实准备、努力沉淀的人。学习的需求,从未消失,只是变得更加务实和深入。

这个专栏的初衷很简单:拒绝过时的、流水线式的PDF引流贴,专注于收集和整理当下最新、最真实的前端面试资料。我会在每一份面经和八股文的基础上,尝试从面试官的角度去拆解问题背后的逻辑,而不仅仅是提供一份静态的背诵答案。无论你是校招还是社招,目标是中大厂还是新兴团队,只要是真实发生、有价值的面试经历,我都会在这个专栏里为你沉淀下来。

温馨提示:市面上的面经鱼龙混杂,甄别真伪、把握时效,是我们对抗内卷最有效的武器。

面经原文内容(二面算法 + 三面节选)

📍面试公司:腾讯

🕐面试时间:二面2月11日,三面3月4日

💻面试岗位:前端暑期提前批

❓面试问题(节选):

二面(算法部分)

  1. LRU缓存
  2. 大数相加,自己去写一些测试用例并验证
  3. 思考题:现在有假设一栋楼有100层,你有两个玻璃球,有些楼层扔下去球会碎,有些不会碎,你需要利用这两个球,找到那个临界楼层,最优的解法是什么

三面(节选)

  1. 有没有考虑过计费或者说成本,或者说对于服务端的压力,比如说CDN的(针对实习亮点)

  2. 对于国际化开发和国内开发的区别,有没有什么心得感受

  3. 多语言工具用的是什么,原理是什么

  4. 怎么判断用户当前应该使用的是什么语言

  5. 对于跨端架构的几种方案,如何进行选择(h5,native等)

  6. 现在在跨端架构中如果使用webview加载离线包的方案,如果在端内点击一个下载按钮,整个调用链路和过程是怎么样的

  7. 端侧的方法是如何注入到web中的

  8. langchain.js框架解决了什么事情

来源:牛客网 喜喜玺玺

📝 腾讯前端暑期提前批·二面算法与三面节选深度解析

🎯 面试整体画像

维度 特征
面试风格 二面:算法实战型 + 思维拓展型 三面:成本意识型 + 跨端深入型 + AI工程型
难度评级 ⭐⭐⭐⭐(四到五星,覆盖面广且有深度)
考察重心 数据结构、大数处理、逻辑思维、成本意识、国际化、跨端通信、AI框架

木木有话说:这场面试比较有价值,我分成了上下两篇去讲,二三面有一半是根据个人项目去提问的,这里我没有整理,大家可以去原文看看题型进行准备。虽然环境不行,但是坚持总有回报,感谢原UP的分享,也恭喜他成功获得offer。

🔍 逐题深度解析

二面·算法1:LRU缓存

问题:实现LRU缓存
javascript 复制代码
// LRU = Least Recently Used,最近最少使用

// 1. 使用Map实现(利用Map的插入顺序)
class LRUCache {
  constructor(capacity) {
    this.capacity = capacity
    this.cache = new Map()
  }
  
  get(key) {
    if (!this.cache.has(key)) return -1
    
    // 更新访问顺序:先删除再添加
    const value = this.cache.get(key)
    this.cache.delete(key)
    this.cache.set(key, value)
    return value
  }
  
  put(key, value) {
    if (this.cache.has(key)) {
      // 已存在,删除旧的
      this.cache.delete(key)
    } else if (this.cache.size >= this.capacity) {
      // 缓存已满,删除最久未使用的(Map的第一个)
      const firstKey = this.cache.keys().next().value
      this.cache.delete(firstKey)
    }
    
    // 添加新项
    this.cache.set(key, value)
  }
}

// 2. 使用双向链表 + Map(面试手写推荐)
class ListNode {
  constructor(key, value) {
    this.key = key
    this.value = value
    this.prev = null
    this.next = null
  }
}

class LRUCache {
  constructor(capacity) {
    this.capacity = capacity
    this.cache = new Map() // key -> node
    this.head = new ListNode()
    this.tail = new ListNode()
    this.head.next = this.tail
    this.tail.prev = this.head
  }
  
  // 移动到头部(最近使用)
  moveToHead(node) {
    this.removeNode(node)
    this.addToHead(node)
  }
  
  addToHead(node) {
    node.prev = this.head
    node.next = this.head.next
    this.head.next.prev = node
    this.head.next = node
  }
  
  removeNode(node) {
    node.prev.next = node.next
    node.next.prev = node.prev
  }
  
  removeTail() {
    const node = this.tail.prev
    this.removeNode(node)
    return node
  }
  
  get(key) {
    if (!this.cache.has(key)) return -1
    
    const node = this.cache.get(key)
    this.moveToHead(node)
    return node.value
  }
  
  put(key, value) {
    if (this.cache.has(key)) {
      const node = this.cache.get(key)
      node.value = value
      this.moveToHead(node)
    } else {
      const node = new ListNode(key, value)
      this.cache.set(key, node)
      this.addToHead(node)
      
      if (this.cache.size > this.capacity) {
        const tail = this.removeTail()
        this.cache.delete(tail.key)
      }
    }
  }
}

// 3. 测试
const cache = new LRUCache(2)
cache.put(1, 1)
cache.put(2, 2)
console.log(cache.get(1)) // 1
cache.put(3, 3)           // 删除key 2
console.log(cache.get(2)) // -1

二面·算法2:大数相加

问题:大数相加,自己写测试用例并验证
javascript 复制代码
// 大数相加:处理超过Number.MAX_SAFE_INTEGER的整数

// 1. 字符串相加(从后往前)
function addStrings(num1, num2) {
  let i = num1.length - 1
  let j = num2.length - 1
  let carry = 0
  let result = []
  
  while (i >= 0 || j >= 0 || carry > 0) {
    const digit1 = i >= 0 ? Number(num1[i]) : 0
    const digit2 = j >= 0 ? Number(num2[j]) : 0
    const sum = digit1 + digit2 + carry
    
    result.push(sum % 10)
    carry = Math.floor(sum / 10)
    
    i--
    j--
  }
  
  return result.reverse().join('')
}

// 2. 测试用例
function testAddStrings() {
  const cases = [
    { num1: '123', num2: '456', expected: '579' },
    { num1: '999', num2: '1', expected: '1000' },
    { num1: '0', num2: '0', expected: '0' },
    { num1: '12345678901234567890', num2: '98765432109876543210', expected: '111111111011111111100' },
    { num1: '9', num2: '9', expected: '18' },
    { num1: '100000000000000000000', num2: '1', expected: '100000000000000000001' }
  ]
  
  cases.forEach(({ num1, num2, expected }, index) => {
    const result = addStrings(num1, num2)
    const passed = result === expected
    console.log(`测试用例 ${index + 1}: ${passed ? '✅' : '❌'}`)
    console.log(`  输入: ${num1} + ${num2}`)
    console.log(`  期望: ${expected}`)
    console.log(`  实际: ${result}`)
    console.log('---')
  })
}

testAddStrings()

// 3. 扩展:处理小数
function addBigNumbers(num1, num2) {
  // 分离整数和小数部分
  const [int1, frac1 = ''] = num1.split('.')
  const [int2, frac2 = ''] = num2.split('.')
  
  // 补齐小数位
  const maxFracLen = Math.max(frac1.length, frac2.length)
  const paddedFrac1 = frac1.padEnd(maxFracLen, '0')
  const paddedFrac2 = frac2.padEnd(maxFracLen, '0')
  
  // 整数和小数分别相加
  const intSum = addStrings(int1 || '0', int2 || '0')
  const fracSum = addStrings(paddedFrac1, paddedFrac2)
  
  // 处理小数进位
  let carry = 0
  let fracResult = fracSum
  if (fracSum.length > maxFracLen) {
    carry = 1
    fracResult = fracSum.slice(1)
  }
  
  // 整数部分加上进位
  const finalInt = addStrings(intSum, carry.toString())
  
  return fracResult ? `${finalInt}.${fracResult}` : finalInt
}

二面·思考题:两个玻璃球找临界楼层

问题:100层楼,两个玻璃球,找临界楼层(球会碎的楼层)
javascript 复制代码
// 问题分析
// 有100层楼,从某个楼层F开始往下扔球会碎,F以下不会碎
// 有两个相同的球,找到F的最小尝试次数

// 1. 错误解法:二分法
// 第一个球在50层扔,如果碎了,第二个球要从1层开始逐层试(最多50次)
// 最坏情况:50次

// 2. 最优解法:等间隔法
// 思路:让第一次尝试的间隔逐渐减小,保证最坏情况下的尝试次数最小
// 设第一次尝试的间隔为x,如果碎了,第二个球需要试x-1次
// 如果没碎,下一次尝试间隔x-1,以此类推
// 总次数 = x + (x-1) + (x-2) + ... + 1 >= 100
// 解方程:x(x+1)/2 >= 100 => x >= 14

// 3. 具体策略
// - 第一个球在14层扔
//   - 如果碎了,第二个球从1层开始试到13层(最多13次)
// - 如果没碎,下一个在27层(14+13)扔
//   - 如果碎了,第二个球从15层试到26层(最多12次)
// - 以此类推:14, 27, 39, 50, 60, 69, 77, 84, 90, 95, 99, 100

// 4. 最坏情况
// 最坏需要14次

// 5. 代码实现
function findCriticalFloor(totalFloors = 100) {
  // 计算初始间隔
  let x = Math.ceil((Math.sqrt(8 * totalFloors + 1) - 1) / 2)
  let step = x
  let prevFloor = 0
  let attempts = 0
  
  console.log(`尝试策略:间隔递减法,初始间隔 ${x}`)
  
  // 第一个球
  while (step > 0) {
    const floor = prevFloor + step
    attempts++
    console.log(`第${attempts}次尝试:在${floor}层扔第一个球`)
    
    // 模拟:假设临界楼层是某个值
    // 这里只是演示算法,实际不会知道是否碎了
    
    prevFloor = floor
    step--
  }
  
  return { maxAttempts: x, strategy: '间隔递减法' }
}

findCriticalFloor()

三面·问题3:成本与服务端压力

问题:有没有考虑过计费或者说成本,或者说对于服务端的压力,比如说CDN的
javascript 复制代码
// 1. 成本考虑维度
// 1.1 CDN成本
// - 流量费用:按GB计费
// - 请求次数:按万次计费
// - 边缘节点存储费用

// 1.2 优化策略
// - 缓存命中率优化
const cacheHitRate = (cacheHits / totalRequests) * 100
// 目标:提升到95%以上

// - 压缩减少流量
// Gzip/Brotli压缩,减少70%体积

// - 图片格式优化
// WebP比JPEG小30%,AVIF更小

// - 资源合并减少请求
// 合并CSS/JS,减少请求数

// 2. 服务端压力
// 2.1 指标监控
// - QPS(每秒查询数)
// - 响应时间
// - CPU/内存使用率
// - 数据库连接数

// 2.2 优化方案
// - 缓存策略
// - 数据库索引优化
// - 异步处理
// - 服务端渲染缓存

// 3. 具体案例
// 优化前:CDN月费用 5000元,源站QPS峰值 2000
// 优化后:CDN月费用 3000元(-40%),源站QPS峰值 800(-60%)

三面·问题4-6:国际化开发

问题:国际化开发区别、多语言工具、语言判断
javascript 复制代码
// 1. 国际化 vs 国内开发区别
// 1.1 技术层面
// - 多语言支持:i18n
// - 双向文本:RTL布局
// - 日期/时间格式:UTC转换
// - 货币格式:汇率处理
// - 字体支持:不同字符集

// 1.2 业务层面
// - 合规要求:GDPR等
// - 支付方式:多样化
// - 本地化运营

// 2. 多语言工具
// 2.1 i18next(最常用)
import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'

i18n.use(initReactI18next).init({
  resources: {
    en: { translation: { hello: 'Hello' } },
    zh: { translation: { hello: '你好' } }
  },
  lng: 'zh',
  fallbackLng: 'en'
})

// 2.2 原理
// - 根据key加载对应语言的JSON文件
// - 运行时替换占位符
// - 支持复数、变量插值

// 3. 判断用户语言
// 3.1 浏览器语言
const browserLang = navigator.language || navigator.userLanguage
// 'zh-CN', 'en-US'

// 3.2 优先顺序
function getUserLanguage() {
  // 1. 用户设置
  const savedLang = localStorage.getItem('language')
  if (savedLang) return savedLang
  
  // 2. 浏览器语言
  const browserLang = navigator.language.split('-')[0]
  if (['zh', 'en', 'ja'].includes(browserLang)) {
    return browserLang
  }
  
  // 3. 默认
  return 'en'
}

// 3.3 服务端判断
// 通过Accept-Language头
app.get('/', (req, res) => {
  const acceptLang = req.headers['accept-language']
  // 'zh-CN,zh;q=0.9,en;q=0.8'
  const preferredLang = parseAcceptLanguage(acceptLang)
})

三面·问题8-10:跨端架构

问题:跨端方案选择、离线包下载链路、端方法注入
javascript 复制代码
// 1. 跨端方案对比

| 方案 | 优点 | 缺点 | 适用场景 |
|------|------|------|----------|
| H5 | 开发快、更新灵活 | 性能一般、依赖网络 | 营销页、轻应用 |
| 离线包H5 | 速度快、离线可用 | 包大小限制 | 核心功能H5 |
| React Native | 性能接近原生 | 调试复杂 | 复杂交互应用 |
| Flutter | 性能好、跨端一致 | 包体积大 | 高性能要求 |
| 小程序 | 生态好 | 厂商限制 | 微信生态 |
| 原生 | 性能最好 | 成本高 | 核心体验 |

// 2. 离线包下载链路
// 点击下载按钮后的完整流程

2.1 端内点击下载按钮
2.2 JS调用端API(通过JSBridge)
2.3 端侧发起下载请求
2.4 下载管理器创建任务
2.5 请求CDN获取离线包
2.6 校验包完整性
2.7 解压到本地
2.8 更新离线包索引
2.9 通知JS下载完成
2.10 跳转到本地页面

// 3. 端方法注入(JSBridge实现)
// 3.1 安卓注入
// 原生代码
webView.addJavascriptInterface(new Object() {
    @JavascriptInterface
    public void download(String url) {
        // 执行下载
    }
}, "NativeBridge")

// Web端调用
window.NativeBridge.download('https://example.com/file.zip')

// 3.2 iOS注入
// WKWebView配置
WKUserContentController *controller = [[WKUserContentController alloc] init];
[controller addScriptMessageHandler:self name:@"nativeBridge"];

// Web端调用
window.webkit.messageHandlers.nativeBridge.postMessage({
  action: 'download',
  url: 'https://example.com/file.zip'
})

// 3.3 JSBridge封装
class JSBridge {
  constructor() {
    this.callbacks = {}
    this.callId = 0
    this.init()
  }
  
  init() {
    // 注册全局回调
    window._handleNativeCallback = (callId, data) => {
      const callback = this.callbacks[callId]
      if (callback) {
        callback(data)
        delete this.callbacks[callId]
      }
    }
  }
  
  callNative(action, params, callback) {
    const callId = this.callId++
    this.callbacks[callId] = callback
    
    const message = { callId, action, params }
    
    if (window.NativeBridge) {
      // 安卓
      window.NativeBridge.postMessage(JSON.stringify(message))
    } else if (window.webkit) {
      // iOS
      window.webkit.messageHandlers.nativeBridge.postMessage(message)
    }
  }
}

// 使用
const bridge = new JSBridge()
bridge.callNative('download', { url: 'file.zip' }, (result) => {
  console.log('下载完成', result)
})

三面·问题14:langchain.js

问题:langchain.js框架解决了什么事情
javascript 复制代码
// 1. LangChain是什么?
// 一个用于开发LLM应用的框架,提供模块化组件

// 2. 核心能力

// 2.1 链式调用
import { Chain } from 'langchain/chains'
import { OpenAI } from 'langchain/llms'

const model = new OpenAI({ temperature: 0 })
const chain = new Chain({
  llm: model,
  prompt: '请翻译成英文: {text}'
})

const result = await chain.call({ text: '你好' })

// 2.2 检索增强生成(RAG)
import { RetrievalQAChain } from 'langchain/chains'
import { HNSWLib } from 'langchain/vectorstores'
import { OpenAIEmbeddings } from 'langchain/embeddings'

// 创建向量存储
const vectorStore = await HNSWLib.fromDocuments(
  documents,
  new OpenAIEmbeddings()
)

// 创建RAG链
const chain = RetrievalQAChain.fromLLM(
  model,
  vectorStore.asRetriever()
)

// 2.3 工具调用
import { initializeAgentExecutor } from 'langchain/agents'
import { Calculator } from 'langchain/tools'
import { SerpAPI } from 'langchain/tools'

const tools = [new Calculator(), new SerpAPI()]
const executor = await initializeAgentExecutor(
  tools,
  model,
  'zero-shot-react-description'
)

// 3. 解决的问题
// 3.1 简化LLM集成
// 不用自己处理API调用、token计数、重试等

// 3.2 提供标准接口
// 统一不同模型的调用方式

// 3.3 支持复杂流程
// 链式调用、Agent、RAG开箱即用

// 3.4 前端专用版本
import { ChatOpenAI } from 'langchain/chat_models'
import { ConversationChain } from 'langchain/chains'

// 在浏览器中运行
const chain = new ConversationChain({
  llm: new ChatOpenAI({ 
    openAIApiKey: process.env.OPENAI_API_KEY 
  })
})

📚 知识点速查表

知识点 核心要点
LRU缓存 Map实现、双向链表、O(1)操作
大数相加 字符串处理、进位、小数处理
玻璃球问题 间隔递减法、最坏情况最小化
成本优化 CDN费用、缓存命中率、压缩
国际化 i18n、RTL、语言判断、多语言工具
跨端方案 H5、离线包、RN、Flutter对比
JSBridge 方法注入、调用链路、回调处理
LangChain 链式调用、RAG、Agent、简化LLM集成

📌 最后一句:

腾讯这场二面和三面,从算法思维到成本意识,从国际化到跨端通信,再到AI工程化框架,考察的是工程师的综合素养。能答好这些,说明你不仅有代码能力,还有业务视野和技术深度。

相关推荐
bluceli2 小时前
前端国际化(i18n)实战指南:构建多语言应用的完整方案
前端
hh随便起个名2 小时前
React组件通信
前端·react.js·前端框架
前端 贾公子2 小时前
vite-plugin-eruda-pro 在vite中使用eruda
前端
Jackson__2 小时前
Agent Skill 和 Rules 有什么区别?
前端·agent·ai编程
不要卷鸿蒙啊2 小时前
【鸿蒙开发】HMRouter一款和好用的管理路由三方工具
前端·harmonyos
李剑一2 小时前
数字孪生大屏必看:Cesium 3D 模型选中交互,3 种高亮效果拿来就用!
前端·vue.js·cesium
奶昔不会射手3 小时前
自定义vue3函数式弹窗
前端·javascript·css
new code Boy3 小时前
前端全栈之路
前端
牛奶3 小时前
为什么敲几个字母就能访问网站?DNS原理大揭秘
前端·http·dns