📝 面试题 + 常见追问
✅ P0 必考 - 带追问版
🔷 TypeScript(5题)
1. any vs unknown vs never
- 🔍 追问 1:
unknown和any在 IDE 智能提示上有什么区别?为什么? - 🔍 追问 2:什么情况下
never类型会自动推导出来?给个例子 - 🔍 追问 3:你在实际项目中,什么时候会用
unknown而不是any?
2. interface vs type
- 🔍 追问 1:既然
interface可以 declaration merging,这会导致什么问题吗? - 🔍 追问 2:为什么建议用
interface定义 React 组件的 props? - 🔍 追问 3:
type可以用&合并,interface用extends,效果有区别吗?
3. 泛型基础
- 🔍 追问 1:泛型约束(
extends)怎么用?给个实际例子 - 🔍 追问 2:如果泛型参数没传,TypeScript 怎么推导默认类型?
- 🔍 追问 3:为什么有时候泛型推导不出来,需要手动指定?
4. React组件类型定义(FC vs 普通函数)
- 🔍 追问 1:为什么现在不推荐用
React.FC了? - 🔍 追问 2:children 的类型应该怎么定义?
ReactNode还是ReactElement? - 🔍 追问 3:如果组件有泛型 props,怎么写类型?
5. useState/useRef 的泛型使用
- 🔍 追问 1:
useState<string | null>(null)和useState(null)有什么区别? - 🔍 追问 2:
useRef<HTMLDivElement>(null)和useRef<HTMLDivElement | null>(null)的区别? - 🔍 追问 3:为什么
useRef要用.current访问?这是怎么实现的?
6. 事件类型定义
- 🔍 追问 1:
React.ChangeEvent<HTMLInputElement>和原生的Event有什么区别? - 🔍 追问 2:如果要在 onChange 里阻止默认行为,类型怎么写?
- 🔍 追问 3:自定义事件的类型怎么定义?
🔷 异步编程(3题)
1. Promise.all/race/allSettled 的区别
- 🔍 追问 1:
Promise.all遇到一个 reject,其他的 Promise 会继续执行吗? - 🔍 追问 2:如果想要
Promise.all部分失败也继续,怎么办? - 🔍 追问 3:
Promise.race有什么实际应用场景?
-
- 提示:超时控制
2. 竞态条件是什么?如何避免?
- 🔍 追问 1:在搜索框输入时,如何保证只显示最后一次搜索的结果?
- 🔍 追问 2:用
useEffect发请求时,如何避免竞态? - 🔍 追问 3:除了
AbortController,还有其他方法吗?
-
- 提示:请求 ID、闭包
3. AbortController 取消请求
- 🔍 追问 1:取消请求后,Promise 会 reject 吗?怎么处理这个错误?
- 🔍 追问 2:如果用 axios,如何取消请求?
- 🔍 追问 3:React Query 或 SWR 是怎么处理这个问题的?
🔷 Event Loop(2题)
1. 浏览器事件循环,宏任务vs微任务
- 🔍 追问 1:
Promise.then和queueMicrotask有区别吗? - 🔍 追问 2:如果一个微任务里又创建了微任务,会一直执行吗?什么时候停?
- 🔍 追问 3:为什么微任务优先级更高?这样设计有什么好处?
2. Promise/setTimeout/requestAnimationFrame 的执行顺序
- 🔍 追问 1:如果有多个
rAF,它们在什么时候执行? - 🔍 追问 2:
rAF和浏览器渲染的关系是什么? - 🔍 追问 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(),会发生什么? - 🔍 追问 2:
next('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里的fields和references有什么用? - 🔍 追问 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 的
.catch和try/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可以解决吗?为什么? - 🔍 追问 3:
useCallback的依赖数组和闭包有什么关系?
🔷 箭头函数 vs 普通函数(1题)
1. 箭头函数和普通函数的 this 区别
- 🔍 追问 1:箭头函数能用
call/apply/bind改变 this 吗? - 🔍 追问 2:为什么 React 的事件处理函数用箭头函数更好?
- 🔍 追问 3:箭头函数还有哪些限制?(提示:arguments, new)
🔷 数组/对象操作(2题)
1. map/filter/reduce 的实际应用
- 🔍 追问 1:
reduce如何实现数组去重? - 🔍 追问 2:
map和forEach的区别?哪个性能更好? - 🔍 追问 3:能用
reduce实现map和filter吗?
2. 浅拷贝 vs 深拷贝
- 🔍 追问 1:
{...obj}是深拷贝还是浅拷贝? - 🔍 追问 2:
JSON.parse(JSON.stringify(obj))有什么问题? - 🔍 追问 3:如何处理循环引用的深拷贝?
- 🔍 追问 4:
structuredClone知道吗?和手写深拷贝的区别?
🔷 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); // ?