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). 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.
01Event loop của JavaScript hoạt động thế nào? Đoạn code sau in ra theo thứ tự nào?JavaScript & TypeScript
Gợi ý trả lời
Ôn thêm câu hỏi JavaScript & TypeScript →JavaScript chạy trên một thread. Code đồng bộ chạy hết trên call stack trước, sau đó event loop lấy việc từ hàng đợi. Microtask (Promise
.then,await,queueMicrotask) luôn được chạy hết trước khi sang macrotask tiếp theo (setTimeout, sự kiện I/O, sự kiện người dùng).console.log(1); setTimeout(() => console.log(2)); Promise.resolve().then(() => console.log(3)); console.log(4); // 1, 4, 3, 2- Trên trình duyệt, việc render thường diễn ra giữa các macrotask.
- Một vòng lặp đồng bộ dài, hoặc chuỗi microtask không dứt, sẽ làm treo giao diện.
Người phỏng vấn muốn nghe:
setTimeout(fn, 0)không chạy ngay mà chỉ được xếp hàng, và vì sao microtask được ưu tiên.02So sánh
Promise.all,Promise.allSettled,Promise.racevàPromise.any. Xử lý lỗi với async/await thế nào?JavaScript & TypeScriptGợi ý trả lời
Ôn thêm câu hỏi JavaScript & TypeScript →Cả bốn đều chạy nhiều Promise song song, khác nhau ở cách kết thúc:
all: chờ tất cả thành công; một cái lỗi là reject ngay.allSettled: chờ tất cả xong, trả về trạng thái từng cái; không bao giờ reject.race: lấy kết quả (thành công hay lỗi) của cái xong đầu tiên; hay dùng để đặt timeout.any: lấy cái thành công đầu tiên; chỉ reject khi tất cả đều lỗi.
try { const [user, orders] = await Promise.all([getUser(id), getOrders(id)]); } catch (err) { showError(err); }Hay sai:
awaitlần lượt trong vòng lặp các request không phụ thuộc nhau, khiến tổng thời gian cộng dồn. Ngược lại, gọiPromise.allvới hàng nghìn request cùng lúc có thể làm quá tải server; khi đó cần giới hạn số request đồng thời.03Trong 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).04Custom hook là gì? Các quy tắc của hooks là gì và vì sao có chúng?ReactJS
Gợi ý trả lời
Ôn thêm câu hỏi ReactJS →Custom hook là hàm bắt đầu bằng
use, dùng các hook khác bên trong, để tái sử dụng logic có state giữa nhiều component (không chia sẻ state, mỗi component có bản riêng).function useDebounce(value, ms) { const [debounced, setDebounced] = useState(value); useEffect(() => { const id = setTimeout(() => setDebounced(value), ms); return () => clearTimeout(id); }, [value, ms]); return debounced; }Quy tắc: chỉ gọi hook ở cấp cao nhất của component hoặc custom hook; không gọi trong
if, vòng lặp hay saureturnsớm. Lý do: React nhận diện từng hook theo thứ tự gọi, đổi thứ tự là state bị ghép nhầm.Ngoại lệ: hook
use()của React 19 được gọi trong điều kiện.Người phỏng vấn muốn nghe: bạn bật ESLint plugin
react-hooksđể bắt các lỗi này tự động.05Bạn chọn cách quản lý state thế nào: Context, Redux Toolkit, Zustand hay TanStack Query?ReactJS
Gợi ý trả lời
Ôn thêm câu hỏi ReactJS →Trước hết phân loại state:
- Server state (dữ liệu từ API): dùng TanStack Query hoặc SWR. Chúng lo cache, loading, refetch, đồng bộ. Phần lớn "global state" trong app thực ra thuộc loại này.
- UI state cục bộ:
useState/useReducerngay trong component. - State trên URL (bộ lọc, trang): để trên query string, chia sẻ link được.
- Client state toàn cục thật sự (giỏ hàng, theme, user đang đăng nhập): Context nếu ít thay đổi; Zustand hoặc Redux Toolkit nếu thay đổi thường xuyên hoặc logic phức tạp.
Redux Toolkit hợp khi team lớn cần quy ước chặt và DevTools mạnh. Zustand gọn nhẹ hơn.
Hay sai: gom state hay thay đổi vào một Context lớn. Mọi component dùng Context đó render lại mỗi lần bất kỳ giá trị nào đổi. Hãy tách Context, hoặc dùng thư viện có selector.
06Debounce 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.07Dù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 (
08Mộ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).09Stale 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:
10Vì sao bạn chọn ứng tuyển vào công ty chúng tôi?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á →Câu này kiểm tra bạn có tìm hiểu không hay chỉ rải CV. Nối ba thứ: công ty, vị trí, và bạn.
- Tìm hiểu trước: sản phẩm, khách hàng, công nghệ, tin tức gần đây, đánh giá của nhân viên cũ và hiện tại.
- Chọn một hai điểm cụ thể bạn thật sự quan tâm: sản phẩm bạn từng dùng, bài toán kỹ thuật, quy mô người dùng, văn hoá làm việc.
- Nối với kinh nghiệm hoặc mục tiêu của bạn.
Câu mẫu: "Em có dùng ứng dụng của bên mình và thấy phần thanh toán rất mượt. Trong JD có nói team đang mở rộng hệ thống xử lý giao dịch, đúng mảng em đã làm hai năm. Em nghĩ mình đóng góp được ngay và cũng học được nhiều ở quy mô lớn hơn."
Hay sai: "Vì công ty lớn, lương tốt" hoặc khen chung chung áp vào công ty nào cũng được.
Bạn đã phỏng vấn ở VISION Advisory & Analytics?
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