Explore all courses in this category.
Explore all courses in this category.
Nguyễn Đình Cường
0 comment
09 Nov, 2026
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: 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ỗiHoisting 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à 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()); // 2Biế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.
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, 2Vớ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.
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).
obj.method(): this là obj.undefined.call, apply, bind: bạn chỉ định this thủ công.this riêng, dùng this của phạm vi bao quanh.new: this là đối tượng mới được tạo.== 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ờ.
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ợ.
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.
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.
Nguyễn Đình Cường
0 comment