作为面试官,我最怕遇到什么样的候选人?

0 阅读1分钟

作为面试官,我最怕遇到什么样的候选人?

带过大大小小的业务线,也前前后后面试过几百个前端。

说句掏心窝子的话:每次坐在面试官那把椅子上,我最希望的不是找到理由刷掉你,而是找到理由留下你。招到一个靠谱的人,我自己能少掉多少头发——这笔账我算得极其清楚🫡。

但有一些候选人,会在面试的最初 15 分钟就让我产生深深的疲惫感。不是因为技术不行,而是因为一些极其根深蒂固的思维习惯,让我意识到:这个人上了生产,我要花大量精力替他兜底。

以下是几个让我在心里悄悄打叉的真实场景,每一个都附上了能扭转局面的实际做法,继续看👇。


代码是正确的,但没有边界

有一次面试,我给了一道看起来简单的题——写一个带节流的搜索请求函数:

// 候选人的答案,逻辑无误
function useSearch(keyword: string) {
  const [results, setResults] = useState([]);
  
  useEffect(() => {
    const timer = setTimeout(() => {
      fetch(`/api/search?q=${keyword}`)
        .then(res => res.json())
        .then(setResults);
    }, 300);
    
    return () => clearTimeout(timer);
  }, [keyword]);
  
  return results;
}

我没有立刻说不对,而是问了一个后续问题:

用户在弱网下,快速输入了 前端 三个字,第一个字和第三个字的请求都发出去了,但第一个请求在第三个之后回来,会发生什么?

沉默😑。

他意识到了:setResults 会被先回来的旧数据覆盖,用户看到的是一份和当前输入完全不对应的脏结果。但他修复的方案是加一个 loading 状态来遮住这个问题。

这就是我最怕的思维模式——用 UI 掩盖了一个数据层的竞态漏洞。那个 loading 一旦结束,脏数据依然存在,只是用户暂时看不到而已。

正确的方向是用 AbortController 在发起新请求时物理斩断上一个:

function useSearch(keyword: string) {
  const [results, setResults] = useState([]);

  useEffect(() => {
    if (!keyword.trim()) { setResults([]); return; }
    
    const controller = new AbortController();
    const timer = setTimeout(async () => {
      try {
        const res = await fetch(`/api/search?q=${encodeURIComponent(keyword)}`, {
          signal: controller.signal
        });
        if (!res.ok) throw new Error(`HTTP ${res.status}`);
        setResults(await res.json());
      } catch (err) {
        // 主动取消不是错误,不上报
        if (err instanceof Error && err.name !== 'AbortError') {
          reportError(err);
          setResults([]);
        }
      }
    }, 300);

    return () => {
      clearTimeout(timer);
      controller.abort(); // 竞态物理斩断,不是靠时序运气
    };
  }, [keyword]);

  return results;
}

当面试官追问然后会怎样,他不是在为难你。他是在测试你有没有在真实的生产环境里被这个场景坑过。能沿着他的追问方向说出需要处理竞态、我会用 AbortController,这个候选人在我心里就已经脱颖而出了。


技术原理背得极溜,但从未推动过任何改变

我遇到过对 React Fiber 的双缓存树解释得极其流畅的候选人,前后端通信机制、渲染管线、优先级调度——讲得如数家珍。

但我问:你在实际项目里,有没有因为理解了这个机制,解决过一个具体的性能问题?

ChatGPT Image 2026年9月18日 17_37_54.png

他告诉我,他把这块知识用在了背题上,为了应对面试。

这让我极其难受😖——不是因为背题这件事本身,而是背了这么深的原理,却没有往业务里走一步。

一个原理的价值,不在于你能不能复述它,而在于你有没有用它在某个真实的系统里扭转过局面。

能说我用 React.memouseCallback 之前,先用 React DevTools Profiler 确认了具体的重渲染路径,否则乱用反而会因为浅比较的 overhead 拖慢性能的候选人——这才是让我真正坐直身体的时刻。

所以呢,在准备面试时,每一个原理都给自己配上一个使用场景。哪怕使用场景只是一个自己的玩具项目,也比理论知道但没实践过要扎实得多🤷‍♂️。


给了一个方案,但从未想过这个方案会卡在哪里

我喜欢问一道极其开放的场景题:

让你设计一个多标签页之间的登录态同步方案,用户在一个 Tab 里退出登录,其他所有 Tab 要立刻跳转到登录页,你怎么做?

大多数候选人会给出两个答案:

第一种:用 localStorage 监听 storage 事件。方向正确,能解决基本问题。 第二种:用 BroadcastChannel。更现代,API 更干净。

两个答案都没问题。但我接着问:这个方案在什么情况下会失效?

ChatGPT Image 2026年9月18日 17_54_40.png

很少有人能回答出来——

BroadcastChannellocalStoragestorage 事件,在同一个 Tab 内部发出的消息,自己是收不到的。而在某些浏览器隐私模式下、跨域 iframe 场景、或者 PWA 的 ServiceWorker 独立上下文里,这两个通信机制都会完全失效。

真正在生产里维护过多标签页同步的工程师,一定踩过这个坑。他们的答案会带着一种极其具体的危机感:

主要用 BroadcastChannel,但在降级场景下做了 localStorage + storage event 的兜底。同时在每个 Tab 的 visibilitychange 事件里,重新校验一次后端的登录态,用服务端作为最终的单一事实来源,不完全依赖前端跨 Tab 通信——因为这个信道本身就是不可靠的。

能把自己方案的死穴说出来,再给出兜底方案的候选人,无论级别高低,都能在我这里获得极高的评分。


谈崩溃经历时,说的都是接手了一个烂项目😖

我会在面试末尾,问每个候选人同一个开放性问题:

你职业生涯里,遇到过最难搞的技术问题是什么?

ChatGPT Image 2026年9月18日 17_49_13.png

让我极其担忧的回答长这样:

我接手了上家公司的一个远古项目,技术债极多,代码一团乱麻,到处是坑……

这个答案告诉了我他遇到了什么,但完全没有告诉我他做了什么。更重要的是,这种叙述方式隐隐透露出一种思维定势:问题的根源永远在别人那里,我是受害者。

让我坐直身体的回答长这样:

当时我们的首页在 iOS 13 以下的机型上,大约有 1.2% 的用户会触发一个必现的白屏。用 Sentry 看到的是一个 TypeError: undefined is not an object,但调用栈在生产环境被压缩后完全不可读。我花了两天时间,用 BrowserStack 的真机还原了现场,最终定位到是一个第三方 SDK 在旧版 WebKit 下调用了不存在的 ResizeObserver,而我们的 polyfill 加载顺序比 SDK 初始化晚了一步……

排查过程的细节,是工程能力最诚实的证明。 任何人都能说我解决过一个很难的 Bug,但只有真正经历过的人,才能说出具体的工具、具体的路径和具体的死角。


最后说一句

在 2026 年这个 AI 能帮你快速生成八股文答案的年代,面试的淘汰机制已经在悄悄变换维度。

靠刷题和背诵通过的那条路,越来越窄。因为任何 AI 能在 3 秒内搜到的答案,面试官都不会把它当成评分重点。

真正在 2026 年还筛不出来、AI 也无法伪造的东西,只有一种:你在具体的工程现场景里,是否真的遇到过坑、填过坑、在崩溃的系统里解决到天亮🤷‍♂️。

你们觉得呢?

Suggestion.gif


喜欢我的文章,也欢迎关注我的微信公众号:【前端技术官】。

主要分享:前端架构 · AI 编程 · 职场认知 · 开发者成长

前端技术官-ErpanOmer

微信扫码关注 👆

不定期更新,不刷屏,聊点真正有用的干货。