Phỏng vấn JavaScript: closure, hoisting, this và những câu hỏi kinh điển

  • author-image

    Nguyễn Đình Cường

  • blog-tag
  • blog-comment 0 comment
  • created-date 09 Nov, 2026
blog-thumbnail

JavaScript là ngôn ngữ xuất hiện trong hầu hết cuộc phỏng vấn web. Những khái niệm như closure, hoisting hay this nghe có vẻ cũ nhưng vẫn được hỏi liên tục vì chúng kiểm tra xem bạn có thật sự hiểu ngôn ngữ hay không. Bài này giải thích ngắn gọn kèm ví dụ để bạn trả lời gọn.

var, let và const khác nhau thế nào?

  • var: phạm vi theo hàm, được hoisting và khởi tạo là undefined, cho phép khai báo lại.
  • let: phạm vi theo khối, tồn tại vùng "temporal dead zone" trước khi khai báo, gán lại được.
  • const: phạm vi theo khối, không gán lại biến, nhưng nội dung đối tượng vẫn có thể thay đổi.
console.log(a); // undefinedvar a = 1;console.log(b); // ReferenceErrorlet b = 2;const user = { name: 'An' };user.name = 'Binh'; // hợp lệ// user = {};       // lỗi

Hoisting là gì?

Hoisting là hành vi mà khai báo biến và hàm được xử lý trước khi chạy code. Hàm khai báo dạng function foo() {} có thể gọi trước dòng khai báo. Biến var được "kéo lên" nhưng giá trị chưa gán nên là undefined. Với let và const, việc truy cập trước khai báo sẽ ném lỗi.

Closure là gì?

Closure là khả năng một hàm ghi nhớ và truy cập các biến ở phạm vi bên ngoài nơi nó được tạo ra, ngay cả khi hàm bên ngoài đã chạy xong. Đây là nền tảng của nhiều pattern như bộ đếm, module hay xử lý sự kiện.

function makeCounter() {  let count = 0;  return function () {    count += 1;    return count;  };}const counter = makeCounter();console.log(counter()); // 1console.log(counter()); // 2

Biến count không thể bị truy cập trực tiếp từ bên ngoài, nhưng hàm trả về vẫn "nhớ" nó. Đó chính là closure.

Câu hỏi kinh điển: vòng lặp và setTimeout

for (var i = 0; i < 3; i++) {  setTimeout(() => console.log(i), 0);}// in ra 3, 3, 3for (let j = 0; j < 3; j++) {  setTimeout(() => console.log(j), 0);}// in ra 0, 1, 2

Với var, cả ba callback dùng chung một biến i nên khi chạy thì giá trị đã là 3. Với let, mỗi vòng lặp có một biến riêng nên kết quả đúng như mong đợi.

Từ khóa this hoạt động ra sao?

Giá trị của this phụ thuộc vào cách hàm được gọi, không phải nơi nó được định nghĩa (trừ arrow function).

  1. Gọi như phương thức obj.method(): this là obj.
  2. Gọi hàm thường: trong chế độ strict là undefined.
  3. Dùng call, apply, bind: bạn chỉ định this thủ công.
  4. Arrow function: không có this riêng, dùng this của phạm vi bao quanh.
  5. Dùng new: this là đối tượng mới được tạo.

So sánh == và ===

== so sánh sau khi ép kiểu, còn === so sánh cả kiểu và giá trị. Ví dụ 0 == '0' là true, nhưng 0 === '0' là false. Trong thực tế nên mặc định dùng === để tránh bất ngờ.

Sao chép nông và sâu

Gán đối tượng chỉ sao chép tham chiếu. Toán tử spread { ...obj } tạo bản sao nông: các đối tượng lồng bên trong vẫn dùng chung. Muốn sao chép sâu, bạn có thể dùng structuredClone(obj) trong môi trường hỗ trợ.

Mẹo trả lời

  • Định nghĩa ngắn một hai câu, sau đó đưa ví dụ code nhỏ.
  • Nếu có thể, nêu tình huống thực tế dùng khái niệm đó.
  • Nếu quên chi tiết, nói rõ phần bạn chắc chắn và suy luận phần còn lại.

Bài tập tự kiểm tra

Thử đoán kết quả của đoạn code sau trước khi chạy:

const person = {  name: 'An',  hello() { return this.name; },  helloArrow: () => typeof this};const fn = person.hello;console.log(person.hello()); // ?console.log(fn());           // ?

Dòng đầu in ra An vì this là person. Dòng thứ hai tách hàm khỏi đối tượng nên this mất ngữ cảnh: trong chế độ strict sẽ gây lỗi, còn ngoài strict thì this trỏ đến đối tượng toàn cục và kết quả không phải An. Đây là tình huống thực tế khi truyền phương thức làm callback; cách xử lý là dùng bind hoặc arrow function.

Kết luận

Hiểu cơ chế phạm vi, hoisting, closure và this giúp bạn tự tin với phần lớn câu hỏi JavaScript cơ bản. Hãy tự gõ lại các ví dụ ở trên vào trình duyệt, đổi vài chỗ và dự đoán kết quả trước khi chạy.

Muốn học JavaScript bài bản từ nền tảng đến dự án, bạn có thể xem các khóa web tại codebem.com.

author_photo
Nguyễn Đình Cường

0 comment