Việc làm

23/09 · Hà Nội · Tại văn phòng

Junior/Senior Frontend Developer (ReacJS/VueJS/Angular)

SHS · Frontend Developer

Xem mô tả & ứng tuyển trên ITviecMô tả công việc, lương và ứng tuyển nằm ở ITviec.

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.

  1. 01So sánh Promise.all, Promise.allSettled, Promise.race và Promise.any. Xử lý lỗi với async/await thế nào?JavaScript & TypeScript

    Gợi ý trả lời

    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: await lầ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ọi Promise.all vớ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.

    Ôn thêm câu hỏi JavaScript & TypeScript →
  2. 02Trong TypeScript, interface và type khác nhau thế nào? Phân biệt any, unknown và never.JavaScript & TypeScript

    Gợi ý trả lời

    Cả hai đều mô tả hình dạng object và phần lớn trường hợp dùng thay nhau được.

    • interface có thể gộp khai báo (declaration merging) và extends; hợp để mô tả object, API công khai.
    • type mô 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 trong switch.
    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).

    Ôn thêm câu hỏi JavaScript & TypeScript →
  3. 03Debounce 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

    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.

    Ôn thêm câu hỏi JavaScript & TypeScript →
  4. 04Bạ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

    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/useReducer ngay 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.

    Ôn thêm câu hỏi ReactJS →
  5. 05Stale closure trong React là gì? Vì sao setInterval trong useEffect cứ in ra giá trị cũ?ReactJS

    Gợi ý trả lời

    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 đọc count.
    • 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-deps của ESLint cho xong. Cảnh báo đó thường chính là dấu hiệu stale closure.

    Ôn thêm câu hỏi ReactJS →
  6. 06Phân biệt var, let và const. Hoisting và temporal dead zone là gì?JavaScript & TypeScript · Junior

    Gợi ý trả lời

    var có phạm vi hàm, còn let/const có phạm vi block ({}). const không cho gán lại biến, nhưng object hay array bên trong vẫn sửa được.

    • Hoisting: khai báo được "kéo" lên đầu phạm vi. var được khởi tạo sẵn là undefined, nên dùng trước khi khai báo không báo lỗi.
    • let/const cũng được hoist nhưng chưa khởi tạo. Truy cập trước dòng khai báo ném ReferenceError; khoảng đó gọi là temporal dead zone.
    • Function declaration được hoist cả phần thân, nên gọi được trước khi định nghĩa.
    console.log(a); // undefined
    console.log(b); // ReferenceError
    var a = 1;
    let b = 2;
    

    Người phỏng vấn muốn nghe: mặc định dùng const, cần gán lại mới dùng let, và gần như không dùng var trong code mới.

    Ôn thêm câu hỏi JavaScript & TypeScript →
  7. 07== và === khác nhau thế nào? Những giá trị nào là falsy?JavaScript & TypeScript · Junior

    Gợi ý trả lời

    === so sánh cả kiểu lẫn giá trị. == ép kiểu trước khi so, nên cho ra kết quả khó đoán: "0" == 0 là true, [] == false cũng true.

    • Các giá trị falsy: false, 0, -0, 0n, "", null, undefined, NaN. Mọi thứ khác là truthy, kể cả [], {} và "0".
    • NaN === NaN là false; dùng Number.isNaN(x).
    • Ngoại lệ hay được chấp nhận: x == null để bắt cả null lẫn undefined.

    Hay sai: viết const page = input || 1. Khi người dùng nhập 0, giá trị bị thay bằng 1. Dùng nullish coalescing input ?? 1 để chỉ thay khi là null hoặc undefined.

    Ôn thêm câu hỏi JavaScript & TypeScript →
  8. 08Closure là gì? Giải thích kết quả của vòng lặp for (var i...) với setTimeout.JavaScript & TypeScript · Junior

    Gợi ý trả lời

    Closure là khi một hàm "nhớ" được các biến ở phạm vi nơi nó được tạo ra, kể cả sau khi hàm bên ngoài đã chạy xong.

    for (var i = 0; i < 3; i++) {
      setTimeout(() => console.log(i), 0);
    }
    // In ra 3, 3, 3
    
    • var chỉ có một biến i cho cả vòng lặp. Khi callback chạy, vòng lặp đã xong và i bằng 3.
    • Đổi sang let thì mỗi vòng lặp có một i riêng, in ra 0, 1, 2.

    Ứng dụng thực tế: tạo biến private, hàm factory, debounce, memoize, và các hook trong React.

    Hay sai: closure giữ tham chiếu đến biến chứ không chụp lại giá trị. Closure giữ object lớn quá lâu (ví dụ trong listener không gỡ) cũng là nguồn rò rỉ bộ nhớ.

    Ôn thêm câu hỏi JavaScript & TypeScript →
  9. 09React render và cập nhật DOM thế nào? Vì sao list cần key và không nên dùng index làm key?ReactJS · Junior

    Gợi ý trả lời

    Mỗi lần state hoặc props đổi, React gọi lại component để tạo cây element mới, so sánh với cây cũ (reconciliation), rồi chỉ cập nhật phần DOM thật sự thay đổi.

    • Với list, key cho React biết phần tử nào là phần tử nào giữa hai lần render. Key phải ổn định và duy nhất trong list, thường là id từ dữ liệu.
    • Dùng index làm key chỉ ổn khi list không bao giờ chèn, xoá hay đổi thứ tự.

    Hay sai: dùng index cho list có thể xoá hoặc sắp xếp. React ghép nhầm state của item cũ sang item mới: ô input giữ nội dung của dòng đã bị xoá, checkbox tick sai dòng.

    Mẹo hay được hỏi thêm: đổi key của một component là cách chủ động reset toàn bộ state của nó.

    Ôn thêm câu hỏi ReactJS →
  10. 10Bạn làm việc thế nào khi áp lực cao hoặc deadline gấp?Câu hỏi hành vi & văn hoá

    Gợi ý trả lời

    Cho thấy bạn có cách làm cụ thể, không chỉ nói "em chịu được áp lực".

    • Chia nhỏ việc, ưu tiên phần quan trọng nhất cho mục tiêu.
    • Báo sớm cho lead/PM nếu thấy không kịp, đề xuất cắt phạm vi hoặc xin hỗ trợ.
    • Giữ chất lượng ở phần cốt lõi; ghi lại nợ kỹ thuật để xử lý sau.
    • Giữ bình tĩnh, giao tiếp ngắn gọn, rõ ràng với team.

    Câu mẫu theo STAR: "Trước buổi demo cho khách, team phát hiện lỗi tích hợp chỉ còn hai ngày. Em liệt kê các phần bị ảnh hưởng, cùng PM chọn luồng nào bắt buộc phải chạy cho demo, phần còn lại hẹn bàn giao tuần sau. Demo diễn ra suôn sẻ và khách đồng ý với lịch mới."

    Hay sai: khoe thức trắng nhiều đêm. Người phỏng vấn muốn thấy bạn quản lý được áp lực, không chỉ chịu đựng.

    Ôn thêm câu hỏi Câu hỏi hành vi & văn hoá →

Bạn đã phỏng vấn ở SHS?

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