10 câu hỏi có thể gặp khi phỏng vấn
Chọn theo kỹ năng và cấp của tin (JavaScript & TypeScript, ReactJS, System Design & Kiến trúc). Gợi ý trả lời do Drama Công Ty biên soạn để bạn ôn nhanh, không phải đề của công ty.
01Trong TypeScript,
interfacevàtypekhác nhau thế nào? Phân biệtany,unknownvànever.JavaScript & TypeScriptGợi ý trả lời
Ôn thêm câu hỏi JavaScript & TypeScript →Cả hai đều mô tả hình dạng object và phần lớn trường hợp dùng thay nhau được.
interfacecó thể gộp khai báo (declaration merging) vàextends; hợp để mô tả object, API công khai.typemô tả được union, tuple, kiểu ánh xạ, kiểu điều kiện:type Status = "idle" | "loading".
Ba kiểu đặc biệt:
any: tắt kiểm tra kiểu, lỗi lọt qua compiler.unknown: nhận mọi giá trị nhưng bắt buộc thu hẹp kiểu trước khi dùng. Hợp cho dữ liệu từJSON.parse, API,catch (e).never: giá trị không bao giờ xảy ra; dùng để bắt thiếu case trongswitch.
function assertNever(x: never): never { throw new Error("Unhandled: " + x); }Hay sai: dùng
as SomeTypeđể ép dữ liệu từ API. Đó chỉ là lời hứa với compiler; muốn chắc chắn phải validate lúc runtime (ví dụ bằng Zod).02Debounce và throttle khác nhau thế nào? Mỗi cái dùng trong trường hợp nào?JavaScript & TypeScript
Gợi ý trả lời
Ôn thêm câu hỏi JavaScript & TypeScript →Cả hai giới hạn số lần một hàm chạy khi sự kiện bắn liên tục.
- Debounce: chỉ chạy sau khi sự kiện ngừng một khoảng thời gian. Hợp với ô tìm kiếm gợi ý (gõ xong mới gọi API), validate form, resize xong mới tính lại layout.
- Throttle: chạy tối đa một lần mỗi khoảng thời gian. Hợp với scroll, kéo thả, theo dõi vị trí chuột.
function debounce(fn, ms) { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => fn(...args), ms); }; }Hay sai: tạo hàm debounce mới mỗi lần render (trong React), khiến timer bị reset liên tục và debounce không có tác dụng. Với ô tìm kiếm, nhớ huỷ request cũ (
AbortController) để kết quả cũ không đè kết quả mới.03Dùng generics và type narrowing trong TypeScript thế nào? Cho ví dụ discriminated union.JavaScript & TypeScript · Senior
Gợi ý trả lời
Ôn thêm câu hỏi JavaScript & TypeScript →Generics giúp viết hàm và kiểu dùng lại được mà vẫn giữ thông tin kiểu, thay vì rơi về
any.type Result<T> = | { ok: true; data: T } | { ok: false; error: string }; function show(r: Result<User>) { if (r.ok) return r.data.name; // TS biết có data return r.error; }- Field chung (
ok,type,status) giúp TypeScript thu hẹp kiểu trongif/switch. Đây là cách mô hình hoá trạng thái không thể sai: không có chuyện vừa códatavừa cóerror. - Ràng buộc generic:
<T extends { id: string }>. - Utility types hay dùng:
Partial,Pick,Omit,Record,ReturnType,Awaited. - Type guard tự viết:
function isUser(x: unknown): x is User.
Người phỏng vấn muốn nghe: bạn dùng kiểu để làm trạng thái sai không biểu diễn được, chứ không chỉ để chú thích.
- Field chung (
04Stale closure trong React là gì? Vì sao
setIntervaltronguseEffectcứ in ra giá trị cũ?ReactJSGợi ý trả lời
Ôn thêm câu hỏi ReactJS →Mỗi lần render tạo ra hàm mới, "chụp" lại props và state của lần render đó. Nếu effect chỉ chạy một lần, callback bên trong mãi thấy giá trị lúc mount.
useEffect(() => { const id = setInterval(() => { setCount(count + 1); // count luôn là 0 }, 1000); return () => clearInterval(id); }, []);Cách sửa:
- Dùng functional update:
setCount(c => c + 1), không cần đọccount. - Khai báo đúng dependency (effect chạy lại khi giá trị đổi, nhớ cleanup).
- Với giá trị chỉ cần đọc mới nhất mà không muốn chạy lại effect: lưu vào
useRef.
Hay sai: tắt cảnh báo
exhaustive-depscủa ESLint cho xong. Cảnh báo đó thường chính là dấu hiệu stale closure.- Dùng functional update:
05React Server Components là gì? Trong Next.js App Router, khi nào dùng Server Component và khi nào cần
"use client"?ReactJS · SeniorGợi ý trả lời
Ôn thêm câu hỏi ReactJS →Server Component chạy trên server (lúc build hoặc lúc request), có thể
awaitdữ liệu trực tiếp từ DB hoặc API, và không gửi JavaScript của nó xuống trình duyệt. Trong App Router, component mặc định là Server Component.- Dùng Server Component cho: lấy dữ liệu, render nội dung tĩnh, dùng secret hoặc thư viện nặng chỉ cần ở server.
- Cần
"use client"khi dùng state, effect, sự kiện (onClick), API trình duyệt (window,localStorage). "use client"đánh dấu ranh giới: mọi module được import từ file đó đều vào bundle client.- Server Component có thể truyền Server Component khác vào Client Component qua
children. - Props truyền từ server sang client phải serialize được (không truyền hàm thường; Server Action thì được).
Hay sai: đặt
"use client"ở layout gốc, biến cả app thành client và mất lợi ích. Hãy đẩy phần tương tác xuống các component lá.06Thiết kế rate limiter cho API. Bạn chọn thuật toán nào và đặt ở đâu?System Design & Kiến trúc
Gợi ý trả lời
Ôn thêm câu hỏi System Design & Kiến trúc →Rate limiter bảo vệ hệ thống khỏi lạm dụng và quá tải, thường đặt ở API gateway hoặc middleware, đếm theo user, API key hoặc IP.
Thuật toán:
- Fixed window: đơn giản nhưng bị dồn request ở ranh giới hai cửa sổ.
- Sliding window (log hoặc counter): mượt hơn, tốn bộ nhớ hơn.
- Token bucket: cho phép burst ngắn trong giới hạn tốc độ trung bình, được dùng phổ biến.
Khi chạy nhiều instance, bộ đếm phải dùng chung, thường là Redis. Thao tác kiểm tra và tăng phải atomic (
INCRkèmEXPIRE, hoặc Lua script).Trả về HTTP
429kèm headerRetry-After.Người phỏng vấn muốn nghe: bạn quyết định được khi Redis lỗi thì cho qua (fail-open) hay chặn (fail-closed) và giải thích lý do, đồng thời biết giới hạn riêng cho endpoint đắt như đăng nhập, gửi OTP.
07Một trang web dùng càng lâu càng chậm và tốn RAM. Bạn tìm memory leak ở frontend thế nào?JavaScript & TypeScript · Senior
Gợi ý trả lời
Ôn thêm câu hỏi JavaScript & TypeScript →Memory leak ở frontend thường do thứ gì đó vẫn giữ tham chiếu đến object đáng lẽ đã bỏ đi, nhất là trong SPA vì trang không bao giờ tải lại.
Nguyên nhân hay gặp:
addEventListenertrênwindow/documentmà không gỡ khi component bị huỷ.setInterval, subscription (WebSocket, store) không huỷ.- DOM đã bị gỡ khỏi trang nhưng vẫn được biến JS giữ lại (detached DOM).
- Cache hoặc mảng toàn cục chỉ tăng không giảm.
Cách điều tra với Chrome DevTools:
- Tab Memory: chụp heap snapshot, thực hiện thao tác vài lần, chụp lại, so sánh phần tăng thêm.
- Lọc "Detached" để tìm DOM bị giữ, xem chuỗi retainers để biết ai đang giữ.
- Tab Performance bật Memory để xem heap có giảm sau GC không.
Người phỏng vấn muốn nghe: mỗi tài nguyên đăng ký đều có chỗ huỷ tương ứng (cleanup của effect,
ngOnDestroy,onUnmounted,AbortController).08Core Web Vitals gồm những chỉ số nào? Bạn cải thiện hiệu năng tải trang ra sao?JavaScript & TypeScript · Senior
Gợi ý trả lời
Ôn thêm câu hỏi JavaScript & TypeScript →Ba chỉ số chính đo trải nghiệm thật của người dùng:
- LCP (Largest Contentful Paint): thời gian hiện phần tử lớn nhất. Cải thiện: tối ưu ảnh (định dạng hiện đại, đúng kích thước, ưu tiên tải ảnh hero), giảm thời gian phản hồi server, cache/CDN, render phía server.
- INP (Interaction to Next Paint, thay FID từ 2024): độ trễ khi tương tác. Cải thiện: chia nhỏ tác vụ JS dài, giảm JS chạy trên main thread, tránh render lại quá nhiều.
- CLS (Cumulative Layout Shift): độ xô lệch bố cục. Cải thiện: khai báo kích thước cho ảnh, video, quảng cáo; tránh chèn nội dung đẩy trang xuống; xử lý font.
Ngoài ra: code splitting và lazy load theo route, bỏ thư viện nặng không cần, nén và cache tài nguyên tĩnh.
Người phỏng vấn muốn nghe: bạn đo bằng dữ liệu người dùng thật (field data) lẫn Lighthouse, và biết điểm Lighthouse không phải là mục tiêu cuối.
09Tell me about a technical decision you made and why.Phỏng vấn tiếng Anh
Gợi ý trả lời
Ôn thêm câu hỏi Phỏng vấn tiếng Anh →"In my last project, we needed to send notifications to users after each order. Instead of calling the notification service directly, I proposed using a message queue. The main reason was reliability: if the notification service was down, messages would wait in the queue instead of being lost. It also made the order API faster. The trade-off was more infrastructure to maintain, but the team agreed it was worth it."
Mẹo: nói theo thứ tự vấn đề, các lựa chọn, lý do chọn, đánh đổi. Các cụm hữu ích: "The main reason was…", "The trade-off was…", "We considered… but…". Người Việt hay quên nói phần đánh đổi, trong khi đó là điểm người phỏng vấn chờ nghe.
10Điểm yếu của bạn là gì?Câu hỏi hành vi & văn hoá
Gợi ý trả lời
Ôn thêm câu hỏi Câu hỏi hành vi & văn hoá →Chọn điểm yếu thật, không chí mạng với vị trí, và cho thấy bạn đang cải thiện.
Cấu trúc: điểm yếu, ảnh hưởng đã gặp, việc bạn đang làm để khắc phục, tiến bộ đến nay.
Câu mẫu: "Trước đây em hay ngại hỏi khi bị kẹt, tự loay hoay quá lâu, có lần làm trễ task. Giờ em tự đặt giới hạn: kẹt quá một hai giờ mà không có hướng thì em hỏi đồng đội, kèm những gì đã thử. Từ khi làm vậy em xong việc đúng hạn hơn và học được nhanh hơn từ người khác."
Ví dụ khác có thể dùng: chưa quen nói trước đám đông, tiếng Anh nói chưa trôi chảy, hay ôm việc.
Hay sai: "Điểm yếu của em là quá cầu toàn" (nghe như né câu hỏi), hoặc nêu điểm yếu là kỹ năng cốt lõi của vị trí.
Bạn đã phỏng vấn ở ANDPAD VietNam Co., Ltd?
Kể lại các vòng và câu hỏi đã gặp (ẩn danh) để người sau ôn đúng trọng tâm.
Chia sẻ kinh nghiệm phỏng vấn