Câu hỏi & gợi ý trả lời
Q1. Semantic HTML là gì và tại sao nó lại quan trọng trong phát triển web?
Semantic HTML là việc sử dụng các thẻ HTML phản ánh đúng ý nghĩa ngữ cảnh của nội dung (như `<header>`, `<nav>`, `<article>`, `<main>`, `<footer>` thay vì lạm dụng thẻ `<div>`). Nó quan trọng vì giúp trình đọc màn hình (Screen Readers) hiểu cấu trúc trang tốt hơn cho người dùng khiếm thị (Accessibility - A11y), hỗ trợ bot tìm kiếm thu thập dữ liệu hiệu quả hơn (SEO), và giúp mã nguồn rõ ràng, dễ bảo trì.
Q2. Sự khác biệt giữa `<script>`, `<script async>` và `<script defer>` là gì?
Thẻ `<script>` thông thường sẽ tạm dừng HTML parsing để tải về và thực thi script ngay lập tức. Thuộc tính `async` tải script bất đồng bộ song song với việc parse HTML và thực thi ngay khi tải xong (có thể chặn parsing giữa chừng, không đảm bảo thứ tự thực thi). Thuộc tính `defer` cũng tải bất đồng bộ nhưng chỉ thực thi sau khi HTML đã parse xong hoàn toàn và đảm bảo đúng thứ tự xuất hiện trong tài liệu.
Q3. CSS Box Model là gì và thuộc tính `box-sizing: border-box` giải quyết vấn đề gì?
CSS Box Model là mô hình hộp bao quanh mọi phần tử HTML, gồm 4 lớp từ trong ra ngoài: `content`, `padding`, `border`, và `margin`. Với giá trị mặc định `box-sizing: content-box`, kích thước `width`/`height` chỉ áp dụng cho content, khiến padding và border làm phần tử bị phình to hơn dự tính. Thiết lập `box-sizing: border-box` gộp cả padding và border vào `width`/`height`, giúp kiểm soát kích thước layout chính xác và không bị vỡ giao diện.
Q4. Phân biệt `display: none` và `visibility: hidden` về mặt hiển thị và hiệu năng (Reflow/Repaint)?
`display: none` ẩn hoàn toàn phần tử và loại bỏ nó khỏi Render Tree, giải phóng không gian chiếm dụng trên layout và kích hoạt cả quá trình Reflow lẫn Repaint. Trong khi đó, `visibility: hidden` chỉ ẩn nội dung trực quan nhưng vẫn giữ nguyên diện tích của phần tử trên layout, do đó chỉ kích hoạt Repaint mà không gây ra Reflow.
Q5. CSS Specificity (độ ưu tiên) được tính toán như thế nào?
Specificity là trọng số quyết định rule CSS nào được áp dụng khi có xung đột, tính theo thang điểm 4 cấp độ: Inline style (1000) > ID selector (0100) > Class / Pseudo-class / Attribute selector (0010) > Element / Pseudo-element selector (0001). Cờ `!important` có quyền ghi đè cao nhất và bỏ qua thang tính điểm này, tuy nhiên nên hạn chế sử dụng để tránh làm hỏng kiến trúc CSS.
Q6. Flexbox và CSS Grid khác nhau như thế nào? Khi nào nên sử dụng loại nào?
Flexbox là hệ thống layout một chiều (1D), tối ưu cho việc căn chỉnh và phân bổ không gian theo một trục duy nhất (hàng hoặc cột), phù hợp cho các component nhỏ như navbar, nút bấm, danh sách item. CSS Grid là hệ thống layout hai chiều (2D), cho phép kiểm soát cả hàng và cột đồng thời, lý tưởng cho việc dựng khung tổng thể của trang web (page layout) hoặc các bố cục dạng lưới phức tạp.
Q7. Event Loop trong JavaScript hoạt động như thế nào giữa Call Stack, Microtask Queue và Macrotask Queue?
JavaScript là ngôn ngữ đơn luồng (single-threaded) xử lý code đồng bộ trên Call Stack. Khi gặp tác vụ bất đồng bộ, chúng được chuyển cho Web APIs xử lý và sau đó đẩy callback vào Microtask Queue (Promises, `queueMicrotask`, `MutationObserver`) hoặc Macrotask Queue (`setTimeout`, `setInterval`, I/O). Khi Call Stack trống, Event Loop sẽ vét cạn toàn bộ tác vụ trong Microtask Queue trước, rồi mới lấy 1 tác vụ từ Macrotask Queue sang Call Stack để thực thi.
Q8. Phân biệt `var`, `let`, và `const` trong JavaScript?
`var` có function-scope, được hoist lên đầu và gán giá trị khởi tạo `undefined`, đồng thời cho phép khai báo lại cùng một biến. `let` và `const` có block-scope (giới hạn trong cặp `{}`), cũng được hoist nhưng rơi vào Temporal Dead Zone (TDZ) khiến ta không thể truy cập trước dòng khai báo. Khác với `let`, `const` bắt buộc phải gán giá trị khởi tạo ngay khi khai báo và không thể gán lại (reassign) sang giá trị khác.
Q9. Closure trong JavaScript là gì và hãy nêu một ví dụ ứng dụng thực tế?
Closure là khả năng một hàm bên trong ghi nhớ và truy cập được các biến thuộc lexical scope của hàm cha bao bọc nó, ngay cả khi hàm cha đó đã thực thi xong và thoát khỏi Call Stack. Ứng dụng thực tế phổ biến của Closure bao gồm tạo biến private (đóng gói dữ liệu trong Module pattern), kỹ thuật Currying, Function memoization, hoặc lưu trữ trạng thái trong các hàm `debounce`/`throttle`.
Q10. Từ khóa `this` trong JavaScript được xác định như thế nào?
Giá trị của `this` được xác định tại runtime dựa vào cách thức hàm được gọi: gọi hàm thông thường trỏ tới `window` (hoặc `undefined` trong strict mode); gọi qua method của object (`obj.method()`) thì `this` trỏ tới `obj`; gọi với `new` thì `this` trỏ tới instance mới tạo; và có thể chỉ định rõ ràng qua `call`, `apply`, `bind`. Riêng Arrow Function không có `this` riêng mà tự động kế thừa `this` từ scope cha gần nhất tại thời điểm định nghĩa.
Q11. Event Bubbling, Event Capturing là gì và kỹ thuật Event Delegation hoạt động ra sao?
Khi một sự kiện xảy ra trên DOM, nó đi qua 3 pha: Capturing (từ `window` đi xuống target element), Target, và Bubbling (nổi bọt ngược từ target lên lại `window`). Kỹ thuật Event Delegation tận dụng Event Bubbling bằng cách gắn duy nhất một event listener lên phần tử cha chung thay vì gắn lên từng phần tử con, giúp giảm thiểu bộ nhớ sử dụng và tự động áp dụng sự kiện cho cả các phần tử con được render động sau này.
Q12. Phân biệt Shallow Copy và Deep Copy trong JavaScript? Có những cách nào để thực hiện?
Shallow Copy chỉ sao chép các giá trị nguyên thủy và sao chép tham chiếu (reference) của các object/array lồng nhau, thường dùng Spread operator (`{...obj}`) hoặc `Object.assign()`. Deep Copy sao chép độc lập toàn bộ các tầng dữ liệu lồng nhau mà không giữ lại tham chiếu cũ. Để Deep Copy, có thể dùng API native `structuredClone(obj)`, thư viện `lodash.cloneDeep`, hoặc `JSON.parse(JSON.stringify(obj))` (cách JSON có hạn chế là bỏ qua `undefined`, `function`, `Symbol` và lỗi nếu có circular reference).
Q13. Phân biệt kỹ thuật Debounce và Throttle? Cho ví dụ sử dụng thực tế của từng loại.
Debounce trì hoãn việc thực thi hàm cho đến khi người dùng ngừng kích hoạt sự kiện sau một khoảng thời gian chờ xác định (ví dụ: ô tìm kiếm autocomplete, xử lý sự kiện `resize`). Throttle đảm bảo hàm chỉ được thực thi tối đa một lần trong mỗi chu kỳ thời gian định trước dù sự kiện có bị kích hoạt liên tục (ví dụ: lắng nghe sự kiện `scroll` để tính toán lazy load ảnh hoặc hiệu ứng parallax).