Việc làm

9 giờ trước · Hồ Chí Minh · Tại văn phòng

Frontend Developer (JavaScript, TypeScript, NodeJS)

Sqreem Technologies (VN) Pte. Ltd. · 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, Node.js, Python). 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. 01Dùng generics và type narrowing trong TypeScript thế nào? Cho ví dụ discriminated union.JavaScript & TypeScript · Senior

    Gợi ý trả lời

    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 trong if/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ó data vừ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.

    Ôn thêm câu hỏi JavaScript & TypeScript →
  2. 02Mộ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

    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:

    • addEventListener trên window/document mà 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:

    1. 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.
    2. Lọc "Detached" để tìm DOM bị giữ, xem chuỗi retainers để biết ai đang giữ.
    3. 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).

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

    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.

    Ôn thêm câu hỏi JavaScript & TypeScript →
  4. 04Bạn mở rộng (scale) một API Node.js thế nào? Graceful shutdown là gì và vì sao cần?Node.js · Senior

    Gợi ý trả lời

    Node một process chỉ dùng tốt một core, nên scale theo chiều ngang:

    • Chạy nhiều process (cluster, PM2) hoặc nhiều container sau load balancer, Kubernetes tự tăng giảm số pod.
    • App phải stateless: session, cache, rate limit, lock để ở Redis hoặc DB, không để trong bộ nhớ process.
    • WebSocket nhiều instance cần adapter qua Redis pub/sub để gửi tin giữa các instance.
    • Đặt giới hạn connection pool DB theo tổng số instance.

    Graceful shutdown: khi nhận SIGTERM (deploy, scale down), ngừng nhận request mới, chờ request đang chạy xong, đóng DB và queue, rồi thoát:

    process.on("SIGTERM", () => {
      server.close(async () => {
        await db.close();
        process.exit(0);
      });
    });
    

    Hay sai: không xử lý SIGTERM, mỗi lần deploy lại cắt ngang request và job đang làm dở. Nên đặt thêm timeout buộc thoát nếu chờ quá lâu.

    Ôn thêm câu hỏi Node.js →
  5. 05Khi xây dựng API bằng Node.js, bạn chú ý những vấn đề bảo mật nào?Node.js · Senior

    Gợi ý trả lời

    • Validate mọi input ở biên bằng schema (Zod, Joi, class-validator trong NestJS); chỉ nhận field cho phép để tránh mass assignment.
    • Injection: dùng query có tham số hoặc ORM, không nối chuỗi SQL. Với MongoDB, chặn object toán tử như { "$gt": "" } lọt vào điều kiện truy vấn.
    • Xác thực và phân quyền: kiểm tra quyền trên từng tài nguyên (user này có sở hữu đơn hàng này không), không chỉ kiểm tra đã đăng nhập.
    • Rate limiting cho login, OTP, API tốn tài nguyên.
    • Header bảo mật (Helmet), CORS chỉ mở cho origin cần thiết.
    • Secret để trong biến môi trường hoặc secret manager, không commit; không log token, mật khẩu.
    • Dependency: chạy npm audit hoặc công cụ quét trong CI, khoá phiên bản bằng lockfile.

    Người phỏng vấn muốn nghe: lỗi phân quyền theo đối tượng (IDOR) rất phổ biến, và bạn biết cách phòng nó.

    Ôn thêm câu hỏi Node.js →
  6. 06Một API viết bằng Python (Django hoặc FastAPI) phản hồi chậm. Bạn tối ưu theo thứ tự nào?Python · Senior

    Gợi ý trả lời

    Đo trước, tối ưu chỗ tốn nhất trước. Phần lớn trường hợp nút thắt là DB và gọi mạng, không phải tốc độ của Python.

    1. Đo: APM hoặc tracing (OpenTelemetry) để biết thời gian nằm ở DB, API ngoài hay code. Profile bằng py-spy (gắn vào process đang chạy) hoặc cProfile.
    2. DB: xử lý N+1, thêm index phù hợp, chỉ lấy cột cần (only, values), phân trang.
    3. Cache: Redis cho dữ liệu đọc nhiều, ít đổi; nhớ chiến lược invalidate.
    4. Việc nặng hoặc không cần trả ngay (gửi email, xuất file): đẩy sang Celery hoặc queue.
    5. Gọi nhiều API ngoài: chạy song song, đặt timeout.
    6. Cuối cùng mới tới tối ưu thuật toán, hoặc đưa đoạn nặng CPU sang thư viện C hay service khác.

    Hay sai: viết lại sang async hoặc đổi ngôn ngữ trước khi biết thời gian thực sự bị tiêu ở đâu.

    Ôn thêm câu hỏi Python →
  7. 07Frontend cần chú ý gì về bảo mật: XSS, CSRF, CORS? Nên lưu access token ở đâu?JavaScript & TypeScript · Senior

    Gợi ý trả lời

    • XSS: kẻ tấn công chèn script chạy trên trang của bạn. Phòng: framework tự escape khi render; tránh innerHTML, v-html, dangerouslySetInnerHTML với dữ liệu người dùng (nếu bắt buộc thì sanitize); cấu hình Content-Security-Policy.
    • CSRF: trang lạ khiến trình duyệt gửi request kèm cookie của người dùng. Phòng: cookie SameSite=Lax hoặc Strict, CSRF token, kiểm tra Origin.
    • CORS: là cơ chế trình duyệt nới lỏng same-origin policy, do server cấu hình. Nó không phải lớp bảo vệ API khỏi người gọi ngoài trình duyệt.

    Lưu token:

    • localStorage tiện nhưng script XSS nào cũng đọc được.
    • Cookie HttpOnly + Secure + SameSite thì JS không đọc được, nhưng phải tính đến CSRF.

    Người phỏng vấn muốn nghe: không có chỗ lưu nào an toàn tuyệt đối; chặn XSS mới là việc quan trọng nhất, vì có XSS thì mọi cách lưu đều bị lợi dụng.

    Ôn thêm câu hỏi JavaScript & TypeScript →
  8. 08Phâ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 →
  9. 09== 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 →
  10. 10Bạn nghĩ sao về việc làm thêm giờ (OT)?Câu hỏi hành vi & văn hoá

    Gợi ý trả lời

    Trả lời thẳng thắn và cân bằng: sẵn sàng khi thật sự cần, nhưng không coi OT kéo dài là bình thường.

    Câu mẫu: "Em sẵn sàng làm thêm khi có việc gấp như sự cố production hay trước một đợt release quan trọng. Nhưng nếu OT diễn ra thường xuyên, em nghĩ cần xem lại cách estimate hoặc phạm vi, và em sẽ chủ động nêu điều đó với team. Em cũng muốn hỏi thêm: ở team mình OT thường xảy ra trong trường hợp nào và được tính như thế nào ạ?"

    Đây cũng là lúc bạn tìm hiểu ngược: OT có được trả lương hoặc nghỉ bù theo quy định không, tần suất ra sao. Câu trả lời của họ cho bạn biết nhiều về văn hoá công ty.

    Hay sai: hứa "OT thoải mái" để lấy điểm, rồi vào làm mới thấy không chịu nổi.

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

Bạn đã phỏng vấn ở Sqreem Technologies (VN) Pte. 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