TS 全栈必问面试题

67 阅读6分钟

📝 面试题 + 常见追问

✅ P0 必考 - 带追问版

🔷 TypeScript(5题)

1. any vs unknown vs never

  • 🔍 追问 1unknownany 在 IDE 智能提示上有什么区别?为什么?
  • 🔍 追问 2:什么情况下 never 类型会自动推导出来?给个例子
  • 🔍 追问 3:你在实际项目中,什么时候会用 unknown 而不是 any

2. interface vs type

  • 🔍 追问 1:既然 interface 可以 declaration merging,这会导致什么问题吗?
  • 🔍 追问 2:为什么建议用 interface 定义 React 组件的 props?
  • 🔍 追问 3type 可以用 & 合并,interfaceextends,效果有区别吗?

3. 泛型基础

  • 🔍 追问 1:泛型约束(extends)怎么用?给个实际例子
  • 🔍 追问 2:如果泛型参数没传,TypeScript 怎么推导默认类型?
  • 🔍 追问 3:为什么有时候泛型推导不出来,需要手动指定?

4. React组件类型定义(FC vs 普通函数)

  • 🔍 追问 1:为什么现在不推荐用 React.FC 了?
  • 🔍 追问 2:children 的类型应该怎么定义?ReactNode 还是 ReactElement
  • 🔍 追问 3:如果组件有泛型 props,怎么写类型?

5. useState/useRef 的泛型使用

  • 🔍 追问 1useState<string | null>(null)useState(null) 有什么区别?
  • 🔍 追问 2useRef<HTMLDivElement>(null)useRef<HTMLDivElement | null>(null) 的区别?
  • 🔍 追问 3:为什么 useRef 要用 .current 访问?这是怎么实现的?

6. 事件类型定义

  • 🔍 追问 1React.ChangeEvent<HTMLInputElement> 和原生的 Event 有什么区别?
  • 🔍 追问 2:如果要在 onChange 里阻止默认行为,类型怎么写?
  • 🔍 追问 3:自定义事件的类型怎么定义?

🔷 异步编程(3题)

1. Promise.all/race/allSettled 的区别

  • 🔍 追问 1Promise.all 遇到一个 reject,其他的 Promise 会继续执行吗?
  • 🔍 追问 2:如果想要 Promise.all 部分失败也继续,怎么办?
  • 🔍 追问 3Promise.race 有什么实际应用场景?
    • 提示:超时控制

2. 竞态条件是什么?如何避免?

  • 🔍 追问 1:在搜索框输入时,如何保证只显示最后一次搜索的结果?
  • 🔍 追问 2:用 useEffect 发请求时,如何避免竞态?
  • 🔍 追问 3:除了 AbortController,还有其他方法吗?
    • 提示:请求 ID、闭包

3. AbortController 取消请求

  • 🔍 追问 1:取消请求后,Promise 会 reject 吗?怎么处理这个错误?
  • 🔍 追问 2:如果用 axios,如何取消请求?
  • 🔍 追问 3:React Query 或 SWR 是怎么处理这个问题的?

🔷 Event Loop(2题)

1. 浏览器事件循环,宏任务vs微任务

  • 🔍 追问 1Promise.thenqueueMicrotask 有区别吗?
  • 🔍 追问 2:如果一个微任务里又创建了微任务,会一直执行吗?什么时候停?
  • 🔍 追问 3:为什么微任务优先级更高?这样设计有什么好处?

2. Promise/setTimeout/requestAnimationFrame 的执行顺序

  • 🔍 追问 1:如果有多个 rAF,它们在什么时候执行?
  • 🔍 追问 2rAF 和浏览器渲染的关系是什么?
  • 🔍 追问 3:这段代码的输出是什么?为什么?
console.log('start');
setTimeout(() => console.log('timeout'), 0);
Promise.resolve().then(() => console.log('promise'));
requestAnimationFrame(() => console.log('rAF'));
console.log('end');

🔷 Node.js/Express(4题)

1. Express 中间件执行顺序和 next()

  • 🔍 追问 1:如果不调用 next(),会发生什么?
  • 🔍 追问 2next('route')next() 有什么区别?
  • 🔍 追问 3:如果中间件里 throw error,会被错误处理中间件捕获吗?

2. 实现一个 Express 中间件

  • 🔍 追问 1:异步中间件需要注意什么?
  • 🔍 追问 2:如果中间件修改了 req 对象,后续中间件能访问到吗?
  • 🔍 追问 3:如何写一个可配置的中间件(比如带参数)?

3. Express 错误处理机制

  • 🔍 追问 1:为什么错误处理中间件必须是 4 个参数?
  • 🔍 追问 2:如果异步函数里 throw error,会被错误中间件捕获吗?怎么解决?
  • 🔍 追问 3:如何区分不同类型的错误(404、500、自定义错误)?

4. CommonJS vs ESM 的区别

  • 🔍 追问 1:为什么 ESM 要用 import() 动态导入?CJS 不行吗?
  • 🔍 追问 2__dirname__filename 在 ESM 里怎么用?
  • 🔍 追问 3:Node.js 怎么判断一个文件是 CJS 还是 ESM?

🔷 数据库/ORM(1题)

1. Prisma Schema 基本设计

  • 🔍 追问 1@relation 里的 fieldsreferences 有什么用?
  • 🔍 追问 2:一对多关系怎么定义?多对多呢?
  • 🔍 追问 3:如果想让字段有默认值,怎么写?

🔷 错误处理(1题)

1. try/catch 能否捕获异步错误?

  • 🔍 追问 1:这段代码会捕获到错误吗?为什么?
try {
  setTimeout(() => {
    throw new Error('error');
  }, 0);
} catch (e) {
  console.log('caught:', e);
}
  • 🔍 追问 2:如果用 async/await,try/catch 能捕获吗?
  • 🔍 追问 3:Promise 的 .catchtry/catch 有什么区别?哪个更好?

🔷 性能优化(1题)

1. 防抖和节流的区别?手写防抖

  • 🔍 追问 1:防抖需要考虑 this 绑定和参数传递吗?怎么处理?
  • 🔍 追问 2:如果需要立即执行一次(leading edge),怎么改?
  • 🔍 追问 3:如何实现可以取消的防抖?
  • 🔍 追问 4:节流用在什么场景?能写一个吗?

🔷 闭包与作用域(3题)

1. 什么是闭包?实际应用

  • 🔍 追问 1:闭包会导致内存泄漏吗?什么情况下?
  • 🔍 追问 2:这段代码的输出是什么?为什么?
function createCounter() {
  let count = 0;
  return {
    increment: () => ++count,
    decrement: () => --count
  };
}
const counter = createCounter();
console.log(counter.increment()); // ?
console.log(counter.increment()); // ?

2. 循环中的 setTimeout 问题

  • 🔍 追问 1:为什么用 let 就可以解决?let 做了什么?
  • 🔍 追问 2:除了 let 和 IIFE,还有其他方法吗?
  • 🔍 追问 3:这个问题在实际项目中遇到过吗?什么场景?

3. React Hooks 中的闭包陷阱

  • 🔍 追问 1:为什么 useEffect 里的 state 总是旧的?
  • 🔍 追问 2:用 useRef 可以解决吗?为什么?
  • 🔍 追问 3useCallback 的依赖数组和闭包有什么关系?

🔷 箭头函数 vs 普通函数(1题)

1. 箭头函数和普通函数的 this 区别

  • 🔍 追问 1:箭头函数能用 call/apply/bind 改变 this 吗?
  • 🔍 追问 2:为什么 React 的事件处理函数用箭头函数更好?
  • 🔍 追问 3:箭头函数还有哪些限制?(提示:arguments, new)

🔷 数组/对象操作(2题)

1. map/filter/reduce 的实际应用

  • 🔍 追问 1reduce 如何实现数组去重?
  • 🔍 追问 2mapforEach 的区别?哪个性能更好?
  • 🔍 追问 3:能用 reduce 实现 mapfilter 吗?

2. 浅拷贝 vs 深拷贝

  • 🔍 追问 1{...obj} 是深拷贝还是浅拷贝?
  • 🔍 追问 2JSON.parse(JSON.stringify(obj)) 有什么问题?
  • 🔍 追问 3:如何处理循环引用的深拷贝?
  • 🔍 追问 4structuredClone 知道吗?和手写深拷贝的区别?

🔷 ES6+(3题)

1. 解构赋值

  • 🔍 追问 1:这段代码输出什么?
const { a, b = 5 } = { a: 3 };
console.log(b); // ?
  • 🔍 追问 2:如何解构并重命名变量?
  • 🔍 追问 3:解构嵌套对象时,如果中间层是 null 会怎样?

2. 展开运算符和剩余参数

  • 🔍 追问 1:对象展开会保留 prototype 吗?
  • 🔍 追问 2:数组展开和 concat 有什么区别?
  • 🔍 追问 3:剩余参数必须放在最后吗?为什么?

3. 可选链(?.)和空值合并(??)

  • 🔍 追问 1?.&& 有什么区别?
  • 🔍 追问 2??|| 有什么区别?什么时候用 ??
  • 🔍 追问 3:这段代码的输出是什么?
const obj = { a: 0, b: null };
console.log(obj.a ?? 100);  // ?
console.log(obj.b ?? 100);  // ?
console.log(obj.c ?? 100);  // ?